From 8777ff071f7bb37631baa7b6717ad29961e50911 Mon Sep 17 00:00:00 2001 From: "H.J" Date: Wed, 9 Oct 2024 11:36:55 -0400 Subject: sorting by language --- aoc2015/day-01/day-01.rkt | 16 - aoc2015/day-02/day-02.rkt | 26 - aoc2015/day-03/day-03.rkt | 28 - aoc2015/day-04/day-04.rkt | 18 - aoc2015/day-05/day-05.rkt | 32 - aoc2015/day-06/day-06.rkt | 49 - aoc2015/day-25/day-25.rkt | 8 - aoc2017-gleam/.github/workflows/test.yml | 23 - aoc2017-gleam/.gitignore | 4 - aoc2017-gleam/README.md | 25 - aoc2017-gleam/gleam.toml | 24 - aoc2017-gleam/manifest.toml | 36 - aoc2017-gleam/src/aoc2017_gleam.gleam | 5 - aoc2017-gleam/src/aoc_2017/day_1.gleam | 32 - aoc2017-gleam/src/aoc_2017/day_10.gleam | 77 - aoc2017-gleam/src/aoc_2017/day_11.gleam | 75 - aoc2017-gleam/src/aoc_2017/day_12.gleam | 44 - aoc2017-gleam/src/aoc_2017/day_13.gleam | 58 - aoc2017-gleam/src/aoc_2017/day_14.gleam | 82 - aoc2017-gleam/src/aoc_2017/day_15.gleam | 93 - aoc2017-gleam/src/aoc_2017/day_16.gleam | 104 - aoc2017-gleam/src/aoc_2017/day_17.gleam | 55 - aoc2017-gleam/src/aoc_2017/day_2.gleam | 47 - aoc2017-gleam/src/aoc_2017/day_3.gleam | 87 - aoc2017-gleam/src/aoc_2017/day_4.gleam | 31 - aoc2017-gleam/src/aoc_2017/day_5.gleam | 48 - aoc2017-gleam/src/aoc_2017/day_6.gleam | 65 - aoc2017-gleam/src/aoc_2017/day_7.gleam | 108 - aoc2017-gleam/src/aoc_2017/day_8.gleam | 131 - aoc2017-gleam/src/aoc_2017/day_9.gleam | 48 - aoc2017-gleam/src/helpers/set_state.gleam | 55 - aoc2017-gleam/test/aoc2017_gleam_test.gleam | 12 - aoc2018/day-01/day-01.rkt | 16 - aoc2018/day-02/day-02.rkt | 27 - aoc2018/day-03/day-03.rkt | 51 - aoc2018/day-04/day-04.rkt | 43 - aoc2018/day-05/day-05.rkt | 31 - aoc2018/day-06/day-06.rkt | 1 - aoc2019-gleam/.github/workflows/test.yml | 23 - aoc2019-gleam/.gitignore | 4 - aoc2019-gleam/README.md | 25 - aoc2019-gleam/gleam.toml | 21 - aoc2019-gleam/manifest.toml | 33 - aoc2019-gleam/src/aoc2019_gleam.gleam | 5 - aoc2019-gleam/src/aoc_2019/day_1.gleam | 30 - aoc2019-gleam/src/aoc_2019/day_2.gleam | 81 - aoc2019-gleam/test/aoc2019_gleam_test.gleam | 12 - aoc2019/day-02/day-02.rkt | 32 - aoc2019/day-03/day-03.rkt | 52 - aoc2019/day-04/day-04.rkt | 39 - aoc2019/day-05/day-05.rkt | 1 - aoc2020/day-01/day-01.rkt | 20 - aoc2020/day-02/day-02.rkt | 33 - aoc2020/day-03/day-03.rkt | 16 - aoc2020/day-04/day-04.rkt | 64 - aoc2020/day-05/day-05.rkt | 35 - aoc2020/day-06/day-06.rkt | 22 - aoc2020/day-07/day-07.rkt | 46 - aoc2020/day-08/day-08.ipynb | 220 -- aoc2020/day-09/day-09.ipynb | 166 -- aoc2020/day-10/day-10.rkt | 37 - aoc2020/day-11/day-11.rkt | 60 - aoc2020/day-12/day-12.rkt | 56 - aoc2020/day-13/day-13.rkt | 32 - aoc2020/day-14/day-14.rkt | 11 - aoc2020/day-15/day-15.rkt | 22 - aoc2020/day-16/day-16.rkt | 52 - aoc2021/day-01/day-01.pl | 20 - aoc2021/day-01/day-01.rkt | 20 - aoc2021/day-02/day-02.ex | 32 - aoc2021/day-02/day-02.rkt | 24 - aoc2021/day-03/day-03.rkt | 39 - aoc2021/day-04/day-04.rkt | 51 - aoc2021/day-05/day-05.rkt | 57 - aoc2021/day-06/day-06.ex | 35 - aoc2021/day-06/day-06.livemd | 152 -- aoc2021/day-06/day-06.rkt | 27 - aoc2021/day-06/input.txt | 1 - aoc2021/day-07/day-07.rkt | 28 - aoc2021/day-08/day-08.rkt | 64 - aoc2021/day-09/day-09.livemd | 138 -- aoc2021/day-09/day-09.rkt | 59 - aoc2021/day-09/input.txt | 100 - aoc2021/day-10/day-10.rkt | 57 - aoc2021/day-11/day-11.rkt | 56 - aoc2021/day-12/day-12.rkt | 38 - aoc2021/day-13/day-13.rkt | 57 - aoc2021/day-14/day-14.rkt | 61 - aoc2021/day-15/day-15-list-nodes.rkt | 67 - aoc2021/day-15/day-15.livemd | 287 --- aoc2021/day-15/day-15.rkt | 50 - aoc2021/day-16/day-16.rkt | 97 - aoc2021/day-17/day-17.rkt | 43 - aoc2021/day-18/day-18.rkt | 5 - aoc2021/day-19/day-19.rkt | 48 - aoc2021/day-19/test-scanners | 136 -- aoc2021/day-20/day-20.rkt | 59 - aoc2021/day-21/day-21.rkt | 59 - aoc2021/day-22/day-22.rkt | 32 - aoc2021/day-25/day-25.rkt | 35 - aoc2022/commentary.md | 35 - aoc2022/day-01/day-01.ipynb | 139 -- aoc2022/day-01/day-01.rkt | 20 - aoc2022/day-02/day-02.ipynb | 193 -- aoc2022/day-02/day-02.pl | 72 - aoc2022/day-02/prolog-input.txt | 2500 -------------------- aoc2022/day-03/day-03.ipynb | 168 -- aoc2022/day-04/day-04.ipynb | 148 -- aoc2022/day-05/day-05.ipynb | 187 -- aoc2022/day-05/day-05.rkt | 46 - aoc2022/day-06/day-06.ipynb | 122 - aoc2022/day-06/day-06.rkt | 22 - aoc2022/day-07/day-07.rkt | 36 - aoc2022/day-08/day-08.ipynb | 257 -- aoc2022/day-08/day-08.rkt | 56 - aoc2022/day-09/day-09.rkt | 76 - aoc2022/day-10/day-10.rkt | 43 - aoc2022/day-11/day-11.rkt | 85 - aoc2022/day-12/day-12.rkt | 46 - aoc2022/day-13/day-13.rkt | 28 - aoc2022/day-14/day-14.rkt | 51 - aoc2022/day-15/day-15.rkt | 54 - aoc2022/day-16/day-16.rkt | 107 - aoc2022/day-17/day-17.rkt | 53 - aoc2022/day-17/rock-shapes | 17 - aoc2022/day-18/day-18.rkt | 57 - aoc2022/day-19/day-19.rkt | 11 - aoc2022/day-20/day-20.rkt | 48 - aoc2022/day-21/day-21.rkt | 43 - aoc2022/day-22/day-22.rkt | 35 - aoc2022/day-23/day-23.rkt | 76 - aoc2022/day-25/day-25.rkt | 24 - aoc2023-gleam/.DS_Store | Bin 6148 -> 0 bytes aoc2023-gleam/.github/workflows/test.yml | 23 - aoc2023-gleam/.gitignore | 6 - aoc2023-gleam/README.md | 28 - aoc2023-gleam/gleam.toml | 22 - aoc2023-gleam/manifest.toml | 29 - aoc2023-gleam/src/.gitignore | 1 - aoc2023-gleam/src/day1/.gitignore | 1 - aoc2023-gleam/src/day1/solve.gleam | 57 - aoc2023-gleam/src/day10/.gitignore | 1 - aoc2023-gleam/src/day10/solve.gleam | 177 -- aoc2023-gleam/src/day11/.gitignore | 1 - aoc2023-gleam/src/day11/solve.gleam | 84 - aoc2023-gleam/src/day12/.gitignore | 1 - aoc2023-gleam/src/day12/solve.gleam | 90 - aoc2023-gleam/src/day13/.gitignore | 1 - aoc2023-gleam/src/day13/solve.gleam | 87 - aoc2023-gleam/src/day14/.gitignore | 1 - aoc2023-gleam/src/day14/solve.gleam | 94 - aoc2023-gleam/src/day15/.gitignore | 1 - aoc2023-gleam/src/day15/solve.gleam | 104 - aoc2023-gleam/src/day16/.gitignore | 1 - aoc2023-gleam/src/day16/solve.gleam | 119 - aoc2023-gleam/src/day17/.gitignore | 1 - aoc2023-gleam/src/day17/solve.gleam | 143 -- aoc2023-gleam/src/day18/.gitignore | 1 - aoc2023-gleam/src/day18/solve.gleam | 113 - aoc2023-gleam/src/day19/.gitignore | 1 - aoc2023-gleam/src/day19/solve.gleam | 255 -- aoc2023-gleam/src/day2/.gitignore | 1 - aoc2023-gleam/src/day2/solve.gleam | 66 - aoc2023-gleam/src/day20/.gitignore | 1 - aoc2023-gleam/src/day20/solve.gleam | 251 -- aoc2023-gleam/src/day21/.gitignore | 1 - aoc2023-gleam/src/day21/solve.gleam | 25 - aoc2023-gleam/src/day22/.gitignore | 1 - aoc2023-gleam/src/day22/solve.gleam | 199 -- aoc2023-gleam/src/day23/.gitignore | 1 - aoc2023-gleam/src/day23/solve.gleam | 194 -- aoc2023-gleam/src/day3/.gitignore | 1 - aoc2023-gleam/src/day3/solve.gleam | 180 -- aoc2023-gleam/src/day4/.gitignore | 1 - aoc2023-gleam/src/day4/solve.gleam | 98 - aoc2023-gleam/src/day5/.gitignore | 1 - aoc2023-gleam/src/day5/solve.gleam | 162 -- aoc2023-gleam/src/day6/.gitignore | 1 - aoc2023-gleam/src/day6/solve.gleam | 85 - aoc2023-gleam/src/day7/.gitignore | 1 - aoc2023-gleam/src/day7/solve.gleam | 140 -- aoc2023-gleam/src/day8/.gitignore | 1 - aoc2023-gleam/src/day8/solve.gleam | 91 - aoc2023-gleam/src/day9/.gitignore | 1 - aoc2023-gleam/src/day9/solve.gleam | 70 - aoc2023-gleam/src/utilities/array2d.gleam | 74 - aoc2023-gleam/src/utilities/memo.gleam | 57 - aoc2023-gleam/src/utilities/prioqueue.gleam | 64 - aoc2023-gleam/test/aoc2023_test.gleam | 5 - aoc2023-gleam/test/day1/day1_test.gleam | 57 - aoc2023-gleam/test/day10/day10_test.gleam | 60 - aoc2023-gleam/test/day11/day11_test.gleam | 66 - aoc2023-gleam/test/day12/day12_test.gleam | 48 - aoc2023-gleam/test/day13/day13_test.gleam | 76 - aoc2023-gleam/test/day14/day14_test.gleam | 66 - aoc2023-gleam/test/day15/day15_test.gleam | 42 - aoc2023-gleam/test/day16/day16_test.gleam | 66 - aoc2023-gleam/test/day17/day17_test.gleam | 54 - aoc2023-gleam/test/day18/day18_test.gleam | 74 - aoc2023-gleam/test/day19/day19_test.gleam | 80 - aoc2023-gleam/test/day2/day2_test.gleam | 57 - aoc2023-gleam/test/day20/day20_test.gleam | 55 - aoc2023-gleam/test/day21/day21_test.gleam | 38 - aoc2023-gleam/test/day22/day22_test.gleam | 60 - aoc2023-gleam/test/day23/day23_test.gleam | 92 - aoc2023-gleam/test/day3/day3_test.gleam | 66 - aoc2023-gleam/test/day4/day4_test.gleam | 58 - aoc2023-gleam/test/day5/day5_test.gleam | 112 - aoc2023-gleam/test/day6/day6_test.gleam | 50 - aoc2023-gleam/test/day7/day7_test.gleam | 56 - aoc2023-gleam/test/day8/day8_test.gleam | 61 - aoc2023-gleam/test/day9/day9_test.gleam | 52 - aoc2023-racket/day-01/day-01.rkt | 38 - aoc2023-racket/day-02/day-02-parser.rkt | 55 - aoc2023-racket/day-02/day-02.rkt | 35 - aoc2023-racket/day-03/day-03.rkt | 72 - aoc2023-racket/day-04/day-04.rkt | 40 - aoc2023-racket/day-05/day-05.rkt | 91 - aoc2023-racket/day-06/day-06.rkt | 32 - aoc2023-racket/day-07/day-07.rkt | 82 - aoc2023-racket/day-08/day-08.rkt | 36 - aoc2023-racket/day-09/day-09-polynomial.rkt | 17 - aoc2023-racket/day-09/day-09.rkt | 32 - aoc2023-racket/day-10/day-10.rkt | 97 - aoc2023-racket/day-11/day-11.rkt | 40 - aoc2023-racket/day-12/day-12.rkt | 65 - aoc2023-racket/day-13/day-13.rkt | 47 - aoc2023-racket/day-14/day-14.rkt | 49 - aoc2023-racket/day-15/day-15.rkt | 41 - aoc2023-racket/day-16/day-16.rkt | 70 - aoc2023-racket/day-17/day-17.rkt | 86 - aoc2023-racket/day-18/day-18.rkt | 48 - aoc2023-racket/day-19/day-19.rkt | 134 -- aoc2023-racket/day-20/day-20.rkt | 144 -- aoc2023-racket/day-21/day-21.rkt | 69 - aoc2023-racket/day-22/day-22.rkt | 109 - aoc2023-racket/day-23/day-23.rkt | 89 - aoc2023-racket/day-24/day-24a.rkt | 51 - aoc2023-racket/day-24/day-24b.rkt | 37 - aoc2023-racket/day-25/day-25.rkt | 43 - codingquest2024/.github/workflows/test.yml | 23 - codingquest2024/.gitignore | 5 - codingquest2024/README.md | 25 - codingquest2024/gleam.toml | 22 - codingquest2024/manifest.toml | 17 - codingquest2024/src/codingquest2024.gleam | 5 - codingquest2024/src/day1/solution.gleam | 55 - codingquest2024/src/day2/solution.gleam | 66 - codingquest2024/src/day3/solution.gleam | 50 - codingquest2024/src/day4/solution.gleam | 49 - codingquest2024/src/day5/solution.gleam | 67 - codingquest2024/src/day6/solution.gleam | 86 - codingquest2024/src/day7/solution.gleam | 73 - codingquest2024/src/day8/solution.gleam | 26 - codingquest2024/src/day9/input.txt | 203 -- codingquest2024/src/day9/solution.gleam | 0 codingquest2024/src/utilities/memo.gleam | 57 - codingquest2024/test/codingquest2024_test.gleam | 12 - gleam/aoc2017/.github/workflows/test.yml | 23 + gleam/aoc2017/.gitignore | 4 + gleam/aoc2017/README.md | 25 + gleam/aoc2017/gleam.toml | 24 + gleam/aoc2017/manifest.toml | 36 + gleam/aoc2017/src/aoc2017_gleam.gleam | 5 + gleam/aoc2017/src/aoc_2017/day_1.gleam | 32 + gleam/aoc2017/src/aoc_2017/day_10.gleam | 77 + gleam/aoc2017/src/aoc_2017/day_11.gleam | 75 + gleam/aoc2017/src/aoc_2017/day_12.gleam | 44 + gleam/aoc2017/src/aoc_2017/day_13.gleam | 58 + gleam/aoc2017/src/aoc_2017/day_14.gleam | 82 + gleam/aoc2017/src/aoc_2017/day_15.gleam | 93 + gleam/aoc2017/src/aoc_2017/day_16.gleam | 104 + gleam/aoc2017/src/aoc_2017/day_17.gleam | 55 + gleam/aoc2017/src/aoc_2017/day_2.gleam | 47 + gleam/aoc2017/src/aoc_2017/day_3.gleam | 87 + gleam/aoc2017/src/aoc_2017/day_4.gleam | 31 + gleam/aoc2017/src/aoc_2017/day_5.gleam | 48 + gleam/aoc2017/src/aoc_2017/day_6.gleam | 65 + gleam/aoc2017/src/aoc_2017/day_7.gleam | 108 + gleam/aoc2017/src/aoc_2017/day_8.gleam | 131 + gleam/aoc2017/src/aoc_2017/day_9.gleam | 48 + gleam/aoc2017/src/helpers/set_state.gleam | 55 + gleam/aoc2017/test/aoc2017_gleam_test.gleam | 12 + gleam/aoc2019/.github/workflows/test.yml | 23 + gleam/aoc2019/.gitignore | 4 + gleam/aoc2019/README.md | 25 + gleam/aoc2019/gleam.toml | 21 + gleam/aoc2019/manifest.toml | 33 + gleam/aoc2019/src/aoc2019_gleam.gleam | 5 + gleam/aoc2019/src/aoc_2019/day_1.gleam | 30 + gleam/aoc2019/src/aoc_2019/day_2.gleam | 81 + gleam/aoc2019/test/aoc2019_gleam_test.gleam | 12 + gleam/aoc2023/.DS_Store | Bin 0 -> 6148 bytes gleam/aoc2023/.github/workflows/test.yml | 23 + gleam/aoc2023/.gitignore | 6 + gleam/aoc2023/README.md | 28 + gleam/aoc2023/gleam.toml | 22 + gleam/aoc2023/manifest.toml | 29 + gleam/aoc2023/src/.gitignore | 1 + gleam/aoc2023/src/day1/.gitignore | 1 + gleam/aoc2023/src/day1/solve.gleam | 57 + gleam/aoc2023/src/day10/.gitignore | 1 + gleam/aoc2023/src/day10/solve.gleam | 177 ++ gleam/aoc2023/src/day11/.gitignore | 1 + gleam/aoc2023/src/day11/solve.gleam | 84 + gleam/aoc2023/src/day12/.gitignore | 1 + gleam/aoc2023/src/day12/solve.gleam | 90 + gleam/aoc2023/src/day13/.gitignore | 1 + gleam/aoc2023/src/day13/solve.gleam | 87 + gleam/aoc2023/src/day14/.gitignore | 1 + gleam/aoc2023/src/day14/solve.gleam | 94 + gleam/aoc2023/src/day15/.gitignore | 1 + gleam/aoc2023/src/day15/solve.gleam | 104 + gleam/aoc2023/src/day16/.gitignore | 1 + gleam/aoc2023/src/day16/solve.gleam | 119 + gleam/aoc2023/src/day17/.gitignore | 1 + gleam/aoc2023/src/day17/solve.gleam | 143 ++ gleam/aoc2023/src/day18/.gitignore | 1 + gleam/aoc2023/src/day18/solve.gleam | 113 + gleam/aoc2023/src/day19/.gitignore | 1 + gleam/aoc2023/src/day19/solve.gleam | 255 ++ gleam/aoc2023/src/day2/.gitignore | 1 + gleam/aoc2023/src/day2/solve.gleam | 66 + gleam/aoc2023/src/day20/.gitignore | 1 + gleam/aoc2023/src/day20/solve.gleam | 251 ++ gleam/aoc2023/src/day21/.gitignore | 1 + gleam/aoc2023/src/day21/solve.gleam | 25 + gleam/aoc2023/src/day22/.gitignore | 1 + gleam/aoc2023/src/day22/solve.gleam | 199 ++ gleam/aoc2023/src/day23/.gitignore | 1 + gleam/aoc2023/src/day23/solve.gleam | 194 ++ gleam/aoc2023/src/day3/.gitignore | 1 + gleam/aoc2023/src/day3/solve.gleam | 180 ++ gleam/aoc2023/src/day4/.gitignore | 1 + gleam/aoc2023/src/day4/solve.gleam | 98 + gleam/aoc2023/src/day5/.gitignore | 1 + gleam/aoc2023/src/day5/solve.gleam | 162 ++ gleam/aoc2023/src/day6/.gitignore | 1 + gleam/aoc2023/src/day6/solve.gleam | 85 + gleam/aoc2023/src/day7/.gitignore | 1 + gleam/aoc2023/src/day7/solve.gleam | 140 ++ gleam/aoc2023/src/day8/.gitignore | 1 + gleam/aoc2023/src/day8/solve.gleam | 91 + gleam/aoc2023/src/day9/.gitignore | 1 + gleam/aoc2023/src/day9/solve.gleam | 70 + gleam/aoc2023/src/utilities/array2d.gleam | 74 + gleam/aoc2023/src/utilities/memo.gleam | 57 + gleam/aoc2023/src/utilities/prioqueue.gleam | 64 + gleam/aoc2023/test/aoc2023_test.gleam | 5 + gleam/aoc2023/test/day1/day1_test.gleam | 57 + gleam/aoc2023/test/day10/day10_test.gleam | 60 + gleam/aoc2023/test/day11/day11_test.gleam | 66 + gleam/aoc2023/test/day12/day12_test.gleam | 48 + gleam/aoc2023/test/day13/day13_test.gleam | 76 + gleam/aoc2023/test/day14/day14_test.gleam | 66 + gleam/aoc2023/test/day15/day15_test.gleam | 42 + gleam/aoc2023/test/day16/day16_test.gleam | 66 + gleam/aoc2023/test/day17/day17_test.gleam | 54 + gleam/aoc2023/test/day18/day18_test.gleam | 74 + gleam/aoc2023/test/day19/day19_test.gleam | 80 + gleam/aoc2023/test/day2/day2_test.gleam | 57 + gleam/aoc2023/test/day20/day20_test.gleam | 55 + gleam/aoc2023/test/day21/day21_test.gleam | 38 + gleam/aoc2023/test/day22/day22_test.gleam | 60 + gleam/aoc2023/test/day23/day23_test.gleam | 92 + gleam/aoc2023/test/day3/day3_test.gleam | 66 + gleam/aoc2023/test/day4/day4_test.gleam | 58 + gleam/aoc2023/test/day5/day5_test.gleam | 112 + gleam/aoc2023/test/day6/day6_test.gleam | 50 + gleam/aoc2023/test/day7/day7_test.gleam | 56 + gleam/aoc2023/test/day8/day8_test.gleam | 61 + gleam/aoc2023/test/day9/day9_test.gleam | 52 + gleam/codingquest2024/.github/workflows/test.yml | 23 + gleam/codingquest2024/.gitignore | 5 + gleam/codingquest2024/README.md | 25 + gleam/codingquest2024/gleam.toml | 22 + gleam/codingquest2024/manifest.toml | 17 + gleam/codingquest2024/src/codingquest2024.gleam | 5 + gleam/codingquest2024/src/day1/solution.gleam | 55 + gleam/codingquest2024/src/day2/solution.gleam | 66 + gleam/codingquest2024/src/day3/solution.gleam | 50 + gleam/codingquest2024/src/day4/solution.gleam | 49 + gleam/codingquest2024/src/day5/solution.gleam | 67 + gleam/codingquest2024/src/day6/solution.gleam | 86 + gleam/codingquest2024/src/day7/solution.gleam | 73 + gleam/codingquest2024/src/day8/solution.gleam | 26 + gleam/codingquest2024/src/day9/input.txt | 203 ++ gleam/codingquest2024/src/day9/solution.gleam | 0 gleam/codingquest2024/src/utilities/memo.gleam | 57 + .../test/codingquest2024_test.gleam | 12 + leetcode/lc-1018-binary-prefix.rkt | 11 - leetcode/lc-1037-boomerang.rkt | 21 - leetcode/lc-1185-day-of-week.rkt | 18 - leetcode/lc-1207-unique-occurences.rkt | 10 - leetcode/lc-1221-split-a-string-balanced.rkt | 19 - leetcode/lc-125-valid-palindrome.rkt | 12 - leetcode/lc-1295-even-number-of-digits.rkt | 4 - .../lc-1299-replace-with-greatest-to-right.rkt | 8 - leetcode/lc-1304-find-n-unique-integers.rkt | 5 - leetcode/lc-1436-destination-city.rkt | 14 - leetcode/lc-1450-students-doing-homework.rkt | 12 - leetcode/lc-1460-make-two-arrays-equal.rkt | 4 - leetcode/lc-1496-path-crossing.rkt | 25 - leetcode/lc-1700-students-unable-to-eat.rkt | 36 - leetcode/lc-1812-chessboard-square.rkt | 7 - .../lc-1844-replace-all-digits-with-characters.rkt | 18 - leetcode/lc-1854-max-pop-year.rkt | 17 - leetcode/lc-2-add-two-numbers.rkt | 35 - leetcode/lc-217-contains-duplicate.rkt | 12 - leetcode/lc-228-summary-ranges.rkt | 27 - leetcode/lc-290-word-pattern.rkt | 23 - leetcode/lc-345-reverse-vowels.rkt | 9 - leetcode/lc-349-intersection-of-2-arrays.rkt | 5 - leetcode/lc-36-valid-sudoku.rkt | 33 - leetcode/lc-415-add-strings.rkt | 28 - leetcode/lc-43-multiply-strings.rkt | 28 - leetcode/lc-476-number-complement.rkt | 11 - leetcode/lc-500-keyboard-row.rkt | 23 - leetcode/lc-504-base7.rkt | 16 - leetcode/lc-520-detect-capital.rkt | 12 - leetcode/lc-551-student-attendance-record-1.rkt | 11 - leetcode/lc-58-length-of-last-word.rkt | 7 - leetcode/lc-645-set-mismatch.rkt | 24 - leetcode/lc-657-robot-return.rkt | 17 - leetcode/lc-68-justification.rkt | 44 - leetcode/lc-690-employee-importance.rkt | 14 - leetcode/lc-717-1bit-and-2bit.rkt | 12 - leetcode/lc-745-prefix-suffix.rkt | 27 - leetcode/lc-747-largest-number-twice.rkt | 15 - leetcode/lc-766-toeplitz-matrix.rkt | 9 - leetcode/lc-771-jewels-and-stones.rkt | 6 - leetcode/lc-788-rotated-digits.rkt | 17 - leetcode/lc-796-rotate-string.rkt | 6 - leetcode/lc-819-most-common-word.rkt | 14 - leetcode/lc-836-rectangle-overlap.rkt | 14 - leetcode/lc-844-backspace-string-compare.rkt | 17 - leetcode/lc-896-monotonic-array.rkt | 16 - leetcode/lc-9-palindromic-number.rkt | 38 - leetcode/lc-905-sort-by-parity.rkt | 7 - leetcode/lc-944-delete-columns.rkt | 7 - leetcode/lc-953-alien-dictionary.rkt | 26 - leetcode/lc-989-add-to-array-form.rkt | 24 - leetcode/lc-999-available-captures.rkt | 28 - racket/aoc2015/day-01/day-01.rkt | 16 + racket/aoc2015/day-02/day-02.rkt | 26 + racket/aoc2015/day-03/day-03.rkt | 28 + racket/aoc2015/day-04/day-04.rkt | 18 + racket/aoc2015/day-05/day-05.rkt | 32 + racket/aoc2015/day-06/day-06.rkt | 49 + racket/aoc2015/day-25/day-25.rkt | 8 + racket/aoc2018/day-01/day-01.rkt | 16 + racket/aoc2018/day-02/day-02.rkt | 27 + racket/aoc2018/day-03/day-03.rkt | 51 + racket/aoc2018/day-04/day-04.rkt | 43 + racket/aoc2018/day-05/day-05.rkt | 31 + racket/aoc2018/day-06/day-06.rkt | 1 + racket/aoc2019/day-02/day-02.rkt | 32 + racket/aoc2019/day-03/day-03.rkt | 52 + racket/aoc2019/day-04/day-04.rkt | 39 + racket/aoc2019/day-05/day-05.rkt | 1 + racket/aoc2020/day-01/day-01.rkt | 20 + racket/aoc2020/day-02/day-02.rkt | 33 + racket/aoc2020/day-03/day-03.rkt | 16 + racket/aoc2020/day-04/day-04.rkt | 64 + racket/aoc2020/day-05/day-05.rkt | 35 + racket/aoc2020/day-06/day-06.rkt | 22 + racket/aoc2020/day-07/day-07.rkt | 46 + racket/aoc2020/day-08/day-08.ipynb | 220 ++ racket/aoc2020/day-09/day-09.ipynb | 166 ++ racket/aoc2020/day-10/day-10.rkt | 37 + racket/aoc2020/day-11/day-11.rkt | 60 + racket/aoc2020/day-12/day-12.rkt | 56 + racket/aoc2020/day-13/day-13.rkt | 32 + racket/aoc2020/day-14/day-14.rkt | 11 + racket/aoc2020/day-15/day-15.rkt | 22 + racket/aoc2020/day-16/day-16.rkt | 52 + racket/aoc2021/day-01/day-01.pl | 20 + racket/aoc2021/day-01/day-01.rkt | 20 + racket/aoc2021/day-02/day-02.ex | 32 + racket/aoc2021/day-02/day-02.rkt | 24 + racket/aoc2021/day-03/day-03.rkt | 39 + racket/aoc2021/day-04/day-04.rkt | 51 + racket/aoc2021/day-05/day-05.rkt | 57 + racket/aoc2021/day-06/day-06.ex | 35 + racket/aoc2021/day-06/day-06.livemd | 152 ++ racket/aoc2021/day-06/day-06.rkt | 27 + racket/aoc2021/day-06/input.txt | 1 + racket/aoc2021/day-07/day-07.rkt | 28 + racket/aoc2021/day-08/day-08.rkt | 64 + racket/aoc2021/day-09/day-09.livemd | 138 ++ racket/aoc2021/day-09/day-09.rkt | 59 + racket/aoc2021/day-09/input.txt | 100 + racket/aoc2021/day-10/day-10.rkt | 57 + racket/aoc2021/day-11/day-11.rkt | 56 + racket/aoc2021/day-12/day-12.rkt | 38 + racket/aoc2021/day-13/day-13.rkt | 57 + racket/aoc2021/day-14/day-14.rkt | 61 + racket/aoc2021/day-15/day-15-list-nodes.rkt | 67 + racket/aoc2021/day-15/day-15.livemd | 287 +++ racket/aoc2021/day-15/day-15.rkt | 50 + racket/aoc2021/day-16/day-16.rkt | 97 + racket/aoc2021/day-17/day-17.rkt | 43 + racket/aoc2021/day-18/day-18.rkt | 5 + racket/aoc2021/day-19/day-19.rkt | 48 + racket/aoc2021/day-19/test-scanners | 136 ++ racket/aoc2021/day-20/day-20.rkt | 59 + racket/aoc2021/day-21/day-21.rkt | 59 + racket/aoc2021/day-22/day-22.rkt | 32 + racket/aoc2021/day-25/day-25.rkt | 35 + racket/aoc2022/commentary.md | 35 + racket/aoc2022/day-01/day-01.ipynb | 139 ++ racket/aoc2022/day-01/day-01.rkt | 20 + racket/aoc2022/day-02/day-02.ipynb | 193 ++ racket/aoc2022/day-02/day-02.pl | 72 + racket/aoc2022/day-02/prolog-input.txt | 2500 ++++++++++++++++++++ racket/aoc2022/day-03/day-03.ipynb | 168 ++ racket/aoc2022/day-04/day-04.ipynb | 148 ++ racket/aoc2022/day-05/day-05.ipynb | 187 ++ racket/aoc2022/day-05/day-05.rkt | 46 + racket/aoc2022/day-06/day-06.ipynb | 122 + racket/aoc2022/day-06/day-06.rkt | 22 + racket/aoc2022/day-07/day-07.rkt | 36 + racket/aoc2022/day-08/day-08.ipynb | 257 ++ racket/aoc2022/day-08/day-08.rkt | 56 + racket/aoc2022/day-09/day-09.rkt | 76 + racket/aoc2022/day-10/day-10.rkt | 43 + racket/aoc2022/day-11/day-11.rkt | 85 + racket/aoc2022/day-12/day-12.rkt | 46 + racket/aoc2022/day-13/day-13.rkt | 28 + racket/aoc2022/day-14/day-14.rkt | 51 + racket/aoc2022/day-15/day-15.rkt | 54 + racket/aoc2022/day-16/day-16.rkt | 107 + racket/aoc2022/day-17/day-17.rkt | 53 + racket/aoc2022/day-17/rock-shapes | 17 + racket/aoc2022/day-18/day-18.rkt | 57 + racket/aoc2022/day-19/day-19.rkt | 11 + racket/aoc2022/day-20/day-20.rkt | 48 + racket/aoc2022/day-21/day-21.rkt | 43 + racket/aoc2022/day-22/day-22.rkt | 35 + racket/aoc2022/day-23/day-23.rkt | 76 + racket/aoc2022/day-25/day-25.rkt | 24 + racket/aoc2023/day-01/day-01.rkt | 38 + racket/aoc2023/day-02/day-02-parser.rkt | 55 + racket/aoc2023/day-02/day-02.rkt | 35 + racket/aoc2023/day-03/day-03.rkt | 72 + racket/aoc2023/day-04/day-04.rkt | 40 + racket/aoc2023/day-05/day-05.rkt | 91 + racket/aoc2023/day-06/day-06.rkt | 32 + racket/aoc2023/day-07/day-07.rkt | 82 + racket/aoc2023/day-08/day-08.rkt | 36 + racket/aoc2023/day-09/day-09-polynomial.rkt | 17 + racket/aoc2023/day-09/day-09.rkt | 32 + racket/aoc2023/day-10/day-10.rkt | 97 + racket/aoc2023/day-11/day-11.rkt | 40 + racket/aoc2023/day-12/day-12.rkt | 65 + racket/aoc2023/day-13/day-13.rkt | 47 + racket/aoc2023/day-14/day-14.rkt | 49 + racket/aoc2023/day-15/day-15.rkt | 41 + racket/aoc2023/day-16/day-16.rkt | 70 + racket/aoc2023/day-17/day-17.rkt | 86 + racket/aoc2023/day-18/day-18.rkt | 48 + racket/aoc2023/day-19/day-19.rkt | 134 ++ racket/aoc2023/day-20/day-20.rkt | 144 ++ racket/aoc2023/day-21/day-21.rkt | 69 + racket/aoc2023/day-22/day-22.rkt | 109 + racket/aoc2023/day-23/day-23.rkt | 89 + racket/aoc2023/day-24/day-24a.rkt | 51 + racket/aoc2023/day-24/day-24b.rkt | 37 + racket/aoc2023/day-25/day-25.rkt | 43 + racket/leetcode/lc-1018-binary-prefix.rkt | 11 + racket/leetcode/lc-1037-boomerang.rkt | 21 + racket/leetcode/lc-1185-day-of-week.rkt | 18 + racket/leetcode/lc-1207-unique-occurences.rkt | 10 + .../leetcode/lc-1221-split-a-string-balanced.rkt | 19 + racket/leetcode/lc-125-valid-palindrome.rkt | 12 + racket/leetcode/lc-1295-even-number-of-digits.rkt | 4 + .../lc-1299-replace-with-greatest-to-right.rkt | 8 + racket/leetcode/lc-1304-find-n-unique-integers.rkt | 5 + racket/leetcode/lc-1436-destination-city.rkt | 14 + .../leetcode/lc-1450-students-doing-homework.rkt | 12 + racket/leetcode/lc-1460-make-two-arrays-equal.rkt | 4 + racket/leetcode/lc-1496-path-crossing.rkt | 25 + racket/leetcode/lc-1700-students-unable-to-eat.rkt | 36 + racket/leetcode/lc-1812-chessboard-square.rkt | 7 + .../lc-1844-replace-all-digits-with-characters.rkt | 18 + racket/leetcode/lc-1854-max-pop-year.rkt | 17 + racket/leetcode/lc-2-add-two-numbers.rkt | 35 + racket/leetcode/lc-217-contains-duplicate.rkt | 12 + racket/leetcode/lc-228-summary-ranges.rkt | 27 + racket/leetcode/lc-290-word-pattern.rkt | 23 + racket/leetcode/lc-345-reverse-vowels.rkt | 9 + .../leetcode/lc-349-intersection-of-2-arrays.rkt | 5 + racket/leetcode/lc-36-valid-sudoku.rkt | 33 + racket/leetcode/lc-415-add-strings.rkt | 28 + racket/leetcode/lc-43-multiply-strings.rkt | 28 + racket/leetcode/lc-476-number-complement.rkt | 11 + racket/leetcode/lc-500-keyboard-row.rkt | 23 + racket/leetcode/lc-504-base7.rkt | 16 + racket/leetcode/lc-520-detect-capital.rkt | 12 + .../lc-551-student-attendance-record-1.rkt | 11 + racket/leetcode/lc-58-length-of-last-word.rkt | 7 + racket/leetcode/lc-645-set-mismatch.rkt | 24 + racket/leetcode/lc-657-robot-return.rkt | 17 + racket/leetcode/lc-68-justification.rkt | 44 + racket/leetcode/lc-690-employee-importance.rkt | 14 + racket/leetcode/lc-717-1bit-and-2bit.rkt | 12 + racket/leetcode/lc-745-prefix-suffix.rkt | 27 + racket/leetcode/lc-747-largest-number-twice.rkt | 15 + racket/leetcode/lc-766-toeplitz-matrix.rkt | 9 + racket/leetcode/lc-771-jewels-and-stones.rkt | 6 + racket/leetcode/lc-788-rotated-digits.rkt | 17 + racket/leetcode/lc-796-rotate-string.rkt | 6 + racket/leetcode/lc-819-most-common-word.rkt | 14 + racket/leetcode/lc-836-rectangle-overlap.rkt | 14 + .../leetcode/lc-844-backspace-string-compare.rkt | 17 + racket/leetcode/lc-896-monotonic-array.rkt | 16 + racket/leetcode/lc-9-palindromic-number.rkt | 38 + racket/leetcode/lc-905-sort-by-parity.rkt | 7 + racket/leetcode/lc-944-delete-columns.rkt | 7 + racket/leetcode/lc-953-alien-dictionary.rkt | 26 + racket/leetcode/lc-989-add-to-array-form.rkt | 24 + racket/leetcode/lc-999-available-captures.rkt | 28 + 622 files changed, 18009 insertions(+), 18009 deletions(-) delete mode 100644 aoc2015/day-01/day-01.rkt delete mode 100644 aoc2015/day-02/day-02.rkt delete mode 100644 aoc2015/day-03/day-03.rkt delete mode 100644 aoc2015/day-04/day-04.rkt delete mode 100644 aoc2015/day-05/day-05.rkt delete mode 100644 aoc2015/day-06/day-06.rkt delete mode 100644 aoc2015/day-25/day-25.rkt delete mode 100644 aoc2017-gleam/.github/workflows/test.yml delete mode 100644 aoc2017-gleam/.gitignore delete mode 100644 aoc2017-gleam/README.md delete mode 100644 aoc2017-gleam/gleam.toml delete mode 100644 aoc2017-gleam/manifest.toml delete mode 100644 aoc2017-gleam/src/aoc2017_gleam.gleam delete mode 100644 aoc2017-gleam/src/aoc_2017/day_1.gleam delete mode 100644 aoc2017-gleam/src/aoc_2017/day_10.gleam delete mode 100644 aoc2017-gleam/src/aoc_2017/day_11.gleam delete mode 100644 aoc2017-gleam/src/aoc_2017/day_12.gleam delete mode 100644 aoc2017-gleam/src/aoc_2017/day_13.gleam delete mode 100644 aoc2017-gleam/src/aoc_2017/day_14.gleam delete mode 100644 aoc2017-gleam/src/aoc_2017/day_15.gleam delete mode 100644 aoc2017-gleam/src/aoc_2017/day_16.gleam delete mode 100644 aoc2017-gleam/src/aoc_2017/day_17.gleam delete mode 100644 aoc2017-gleam/src/aoc_2017/day_2.gleam delete mode 100644 aoc2017-gleam/src/aoc_2017/day_3.gleam delete mode 100644 aoc2017-gleam/src/aoc_2017/day_4.gleam delete mode 100644 aoc2017-gleam/src/aoc_2017/day_5.gleam delete mode 100644 aoc2017-gleam/src/aoc_2017/day_6.gleam delete mode 100644 aoc2017-gleam/src/aoc_2017/day_7.gleam delete mode 100644 aoc2017-gleam/src/aoc_2017/day_8.gleam delete mode 100644 aoc2017-gleam/src/aoc_2017/day_9.gleam delete mode 100644 aoc2017-gleam/src/helpers/set_state.gleam delete mode 100644 aoc2017-gleam/test/aoc2017_gleam_test.gleam delete mode 100644 aoc2018/day-01/day-01.rkt delete mode 100644 aoc2018/day-02/day-02.rkt delete mode 100644 aoc2018/day-03/day-03.rkt delete mode 100644 aoc2018/day-04/day-04.rkt delete mode 100644 aoc2018/day-05/day-05.rkt delete mode 100644 aoc2018/day-06/day-06.rkt delete mode 100644 aoc2019-gleam/.github/workflows/test.yml delete mode 100644 aoc2019-gleam/.gitignore delete mode 100644 aoc2019-gleam/README.md delete mode 100644 aoc2019-gleam/gleam.toml delete mode 100644 aoc2019-gleam/manifest.toml delete mode 100644 aoc2019-gleam/src/aoc2019_gleam.gleam delete mode 100644 aoc2019-gleam/src/aoc_2019/day_1.gleam delete mode 100644 aoc2019-gleam/src/aoc_2019/day_2.gleam delete mode 100644 aoc2019-gleam/test/aoc2019_gleam_test.gleam delete mode 100644 aoc2019/day-02/day-02.rkt delete mode 100644 aoc2019/day-03/day-03.rkt delete mode 100644 aoc2019/day-04/day-04.rkt delete mode 100644 aoc2019/day-05/day-05.rkt delete mode 100644 aoc2020/day-01/day-01.rkt delete mode 100644 aoc2020/day-02/day-02.rkt delete mode 100644 aoc2020/day-03/day-03.rkt delete mode 100644 aoc2020/day-04/day-04.rkt delete mode 100644 aoc2020/day-05/day-05.rkt delete mode 100644 aoc2020/day-06/day-06.rkt delete mode 100644 aoc2020/day-07/day-07.rkt delete mode 100644 aoc2020/day-08/day-08.ipynb delete mode 100644 aoc2020/day-09/day-09.ipynb delete mode 100644 aoc2020/day-10/day-10.rkt delete mode 100644 aoc2020/day-11/day-11.rkt delete mode 100644 aoc2020/day-12/day-12.rkt delete mode 100644 aoc2020/day-13/day-13.rkt delete mode 100644 aoc2020/day-14/day-14.rkt delete mode 100644 aoc2020/day-15/day-15.rkt delete mode 100644 aoc2020/day-16/day-16.rkt delete mode 100644 aoc2021/day-01/day-01.pl delete mode 100644 aoc2021/day-01/day-01.rkt delete mode 100644 aoc2021/day-02/day-02.ex delete mode 100644 aoc2021/day-02/day-02.rkt delete mode 100644 aoc2021/day-03/day-03.rkt delete mode 100644 aoc2021/day-04/day-04.rkt delete mode 100644 aoc2021/day-05/day-05.rkt delete mode 100644 aoc2021/day-06/day-06.ex delete mode 100644 aoc2021/day-06/day-06.livemd delete mode 100644 aoc2021/day-06/day-06.rkt delete mode 100644 aoc2021/day-06/input.txt delete mode 100644 aoc2021/day-07/day-07.rkt delete mode 100644 aoc2021/day-08/day-08.rkt delete mode 100644 aoc2021/day-09/day-09.livemd delete mode 100644 aoc2021/day-09/day-09.rkt delete mode 100644 aoc2021/day-09/input.txt delete mode 100644 aoc2021/day-10/day-10.rkt delete mode 100644 aoc2021/day-11/day-11.rkt delete mode 100644 aoc2021/day-12/day-12.rkt delete mode 100644 aoc2021/day-13/day-13.rkt delete mode 100644 aoc2021/day-14/day-14.rkt delete mode 100644 aoc2021/day-15/day-15-list-nodes.rkt delete mode 100644 aoc2021/day-15/day-15.livemd delete mode 100644 aoc2021/day-15/day-15.rkt delete mode 100644 aoc2021/day-16/day-16.rkt delete mode 100644 aoc2021/day-17/day-17.rkt delete mode 100644 aoc2021/day-18/day-18.rkt delete mode 100644 aoc2021/day-19/day-19.rkt delete mode 100644 aoc2021/day-19/test-scanners delete mode 100644 aoc2021/day-20/day-20.rkt delete mode 100644 aoc2021/day-21/day-21.rkt delete mode 100644 aoc2021/day-22/day-22.rkt delete mode 100644 aoc2021/day-25/day-25.rkt delete mode 100644 aoc2022/commentary.md delete mode 100644 aoc2022/day-01/day-01.ipynb delete mode 100644 aoc2022/day-01/day-01.rkt delete mode 100644 aoc2022/day-02/day-02.ipynb delete mode 100644 aoc2022/day-02/day-02.pl delete mode 100644 aoc2022/day-02/prolog-input.txt delete mode 100644 aoc2022/day-03/day-03.ipynb delete mode 100644 aoc2022/day-04/day-04.ipynb delete mode 100644 aoc2022/day-05/day-05.ipynb delete mode 100644 aoc2022/day-05/day-05.rkt delete mode 100644 aoc2022/day-06/day-06.ipynb delete mode 100644 aoc2022/day-06/day-06.rkt delete mode 100644 aoc2022/day-07/day-07.rkt delete mode 100644 aoc2022/day-08/day-08.ipynb delete mode 100644 aoc2022/day-08/day-08.rkt delete mode 100644 aoc2022/day-09/day-09.rkt delete mode 100644 aoc2022/day-10/day-10.rkt delete mode 100644 aoc2022/day-11/day-11.rkt delete mode 100644 aoc2022/day-12/day-12.rkt delete mode 100644 aoc2022/day-13/day-13.rkt delete mode 100644 aoc2022/day-14/day-14.rkt delete mode 100644 aoc2022/day-15/day-15.rkt delete mode 100644 aoc2022/day-16/day-16.rkt delete mode 100644 aoc2022/day-17/day-17.rkt delete mode 100644 aoc2022/day-17/rock-shapes delete mode 100644 aoc2022/day-18/day-18.rkt delete mode 100644 aoc2022/day-19/day-19.rkt delete mode 100644 aoc2022/day-20/day-20.rkt delete mode 100644 aoc2022/day-21/day-21.rkt delete mode 100644 aoc2022/day-22/day-22.rkt delete mode 100644 aoc2022/day-23/day-23.rkt delete mode 100644 aoc2022/day-25/day-25.rkt delete mode 100644 aoc2023-gleam/.DS_Store delete mode 100644 aoc2023-gleam/.github/workflows/test.yml delete mode 100644 aoc2023-gleam/.gitignore delete mode 100644 aoc2023-gleam/README.md delete mode 100644 aoc2023-gleam/gleam.toml delete mode 100644 aoc2023-gleam/manifest.toml delete mode 100644 aoc2023-gleam/src/.gitignore delete mode 100644 aoc2023-gleam/src/day1/.gitignore delete mode 100644 aoc2023-gleam/src/day1/solve.gleam delete mode 100644 aoc2023-gleam/src/day10/.gitignore delete mode 100644 aoc2023-gleam/src/day10/solve.gleam delete mode 100644 aoc2023-gleam/src/day11/.gitignore delete mode 100644 aoc2023-gleam/src/day11/solve.gleam delete mode 100644 aoc2023-gleam/src/day12/.gitignore delete mode 100644 aoc2023-gleam/src/day12/solve.gleam delete mode 100644 aoc2023-gleam/src/day13/.gitignore delete mode 100644 aoc2023-gleam/src/day13/solve.gleam delete mode 100644 aoc2023-gleam/src/day14/.gitignore delete mode 100644 aoc2023-gleam/src/day14/solve.gleam delete mode 100644 aoc2023-gleam/src/day15/.gitignore delete mode 100644 aoc2023-gleam/src/day15/solve.gleam delete mode 100644 aoc2023-gleam/src/day16/.gitignore delete mode 100644 aoc2023-gleam/src/day16/solve.gleam delete mode 100644 aoc2023-gleam/src/day17/.gitignore delete mode 100644 aoc2023-gleam/src/day17/solve.gleam delete mode 100644 aoc2023-gleam/src/day18/.gitignore delete mode 100644 aoc2023-gleam/src/day18/solve.gleam delete mode 100644 aoc2023-gleam/src/day19/.gitignore delete mode 100644 aoc2023-gleam/src/day19/solve.gleam delete mode 100644 aoc2023-gleam/src/day2/.gitignore delete mode 100644 aoc2023-gleam/src/day2/solve.gleam delete mode 100644 aoc2023-gleam/src/day20/.gitignore delete mode 100644 aoc2023-gleam/src/day20/solve.gleam delete mode 100644 aoc2023-gleam/src/day21/.gitignore delete mode 100644 aoc2023-gleam/src/day21/solve.gleam delete mode 100644 aoc2023-gleam/src/day22/.gitignore delete mode 100644 aoc2023-gleam/src/day22/solve.gleam delete mode 100644 aoc2023-gleam/src/day23/.gitignore delete mode 100644 aoc2023-gleam/src/day23/solve.gleam delete mode 100644 aoc2023-gleam/src/day3/.gitignore delete mode 100644 aoc2023-gleam/src/day3/solve.gleam delete mode 100644 aoc2023-gleam/src/day4/.gitignore delete mode 100644 aoc2023-gleam/src/day4/solve.gleam delete mode 100644 aoc2023-gleam/src/day5/.gitignore delete mode 100644 aoc2023-gleam/src/day5/solve.gleam delete mode 100644 aoc2023-gleam/src/day6/.gitignore delete mode 100644 aoc2023-gleam/src/day6/solve.gleam delete mode 100644 aoc2023-gleam/src/day7/.gitignore delete mode 100644 aoc2023-gleam/src/day7/solve.gleam delete mode 100644 aoc2023-gleam/src/day8/.gitignore delete mode 100644 aoc2023-gleam/src/day8/solve.gleam delete mode 100644 aoc2023-gleam/src/day9/.gitignore delete mode 100644 aoc2023-gleam/src/day9/solve.gleam delete mode 100644 aoc2023-gleam/src/utilities/array2d.gleam delete mode 100644 aoc2023-gleam/src/utilities/memo.gleam delete mode 100644 aoc2023-gleam/src/utilities/prioqueue.gleam delete mode 100644 aoc2023-gleam/test/aoc2023_test.gleam delete mode 100644 aoc2023-gleam/test/day1/day1_test.gleam delete mode 100644 aoc2023-gleam/test/day10/day10_test.gleam delete mode 100644 aoc2023-gleam/test/day11/day11_test.gleam delete mode 100644 aoc2023-gleam/test/day12/day12_test.gleam delete mode 100644 aoc2023-gleam/test/day13/day13_test.gleam delete mode 100644 aoc2023-gleam/test/day14/day14_test.gleam delete mode 100644 aoc2023-gleam/test/day15/day15_test.gleam delete mode 100644 aoc2023-gleam/test/day16/day16_test.gleam delete mode 100644 aoc2023-gleam/test/day17/day17_test.gleam delete mode 100644 aoc2023-gleam/test/day18/day18_test.gleam delete mode 100644 aoc2023-gleam/test/day19/day19_test.gleam delete mode 100644 aoc2023-gleam/test/day2/day2_test.gleam delete mode 100644 aoc2023-gleam/test/day20/day20_test.gleam delete mode 100644 aoc2023-gleam/test/day21/day21_test.gleam delete mode 100644 aoc2023-gleam/test/day22/day22_test.gleam delete mode 100644 aoc2023-gleam/test/day23/day23_test.gleam delete mode 100644 aoc2023-gleam/test/day3/day3_test.gleam delete mode 100644 aoc2023-gleam/test/day4/day4_test.gleam delete mode 100644 aoc2023-gleam/test/day5/day5_test.gleam delete mode 100644 aoc2023-gleam/test/day6/day6_test.gleam delete mode 100644 aoc2023-gleam/test/day7/day7_test.gleam delete mode 100644 aoc2023-gleam/test/day8/day8_test.gleam delete mode 100644 aoc2023-gleam/test/day9/day9_test.gleam delete mode 100644 aoc2023-racket/day-01/day-01.rkt delete mode 100644 aoc2023-racket/day-02/day-02-parser.rkt delete mode 100644 aoc2023-racket/day-02/day-02.rkt delete mode 100644 aoc2023-racket/day-03/day-03.rkt delete mode 100644 aoc2023-racket/day-04/day-04.rkt delete mode 100644 aoc2023-racket/day-05/day-05.rkt delete mode 100644 aoc2023-racket/day-06/day-06.rkt delete mode 100644 aoc2023-racket/day-07/day-07.rkt delete mode 100644 aoc2023-racket/day-08/day-08.rkt delete mode 100644 aoc2023-racket/day-09/day-09-polynomial.rkt delete mode 100644 aoc2023-racket/day-09/day-09.rkt delete mode 100644 aoc2023-racket/day-10/day-10.rkt delete mode 100644 aoc2023-racket/day-11/day-11.rkt delete mode 100644 aoc2023-racket/day-12/day-12.rkt delete mode 100644 aoc2023-racket/day-13/day-13.rkt delete mode 100644 aoc2023-racket/day-14/day-14.rkt delete mode 100644 aoc2023-racket/day-15/day-15.rkt delete mode 100644 aoc2023-racket/day-16/day-16.rkt delete mode 100644 aoc2023-racket/day-17/day-17.rkt delete mode 100644 aoc2023-racket/day-18/day-18.rkt delete mode 100644 aoc2023-racket/day-19/day-19.rkt delete mode 100644 aoc2023-racket/day-20/day-20.rkt delete mode 100644 aoc2023-racket/day-21/day-21.rkt delete mode 100644 aoc2023-racket/day-22/day-22.rkt delete mode 100644 aoc2023-racket/day-23/day-23.rkt delete mode 100644 aoc2023-racket/day-24/day-24a.rkt delete mode 100644 aoc2023-racket/day-24/day-24b.rkt delete mode 100644 aoc2023-racket/day-25/day-25.rkt delete mode 100644 codingquest2024/.github/workflows/test.yml delete mode 100644 codingquest2024/.gitignore delete mode 100644 codingquest2024/README.md delete mode 100644 codingquest2024/gleam.toml delete mode 100644 codingquest2024/manifest.toml delete mode 100644 codingquest2024/src/codingquest2024.gleam delete mode 100644 codingquest2024/src/day1/solution.gleam delete mode 100644 codingquest2024/src/day2/solution.gleam delete mode 100644 codingquest2024/src/day3/solution.gleam delete mode 100644 codingquest2024/src/day4/solution.gleam delete mode 100644 codingquest2024/src/day5/solution.gleam delete mode 100644 codingquest2024/src/day6/solution.gleam delete mode 100644 codingquest2024/src/day7/solution.gleam delete mode 100644 codingquest2024/src/day8/solution.gleam delete mode 100644 codingquest2024/src/day9/input.txt delete mode 100644 codingquest2024/src/day9/solution.gleam delete mode 100644 codingquest2024/src/utilities/memo.gleam delete mode 100644 codingquest2024/test/codingquest2024_test.gleam create mode 100644 gleam/aoc2017/.github/workflows/test.yml create mode 100644 gleam/aoc2017/.gitignore create mode 100644 gleam/aoc2017/README.md create mode 100644 gleam/aoc2017/gleam.toml create mode 100644 gleam/aoc2017/manifest.toml create mode 100644 gleam/aoc2017/src/aoc2017_gleam.gleam create mode 100644 gleam/aoc2017/src/aoc_2017/day_1.gleam create mode 100644 gleam/aoc2017/src/aoc_2017/day_10.gleam create mode 100644 gleam/aoc2017/src/aoc_2017/day_11.gleam create mode 100644 gleam/aoc2017/src/aoc_2017/day_12.gleam create mode 100644 gleam/aoc2017/src/aoc_2017/day_13.gleam create mode 100644 gleam/aoc2017/src/aoc_2017/day_14.gleam create mode 100644 gleam/aoc2017/src/aoc_2017/day_15.gleam create mode 100644 gleam/aoc2017/src/aoc_2017/day_16.gleam create mode 100644 gleam/aoc2017/src/aoc_2017/day_17.gleam create mode 100644 gleam/aoc2017/src/aoc_2017/day_2.gleam create mode 100644 gleam/aoc2017/src/aoc_2017/day_3.gleam create mode 100644 gleam/aoc2017/src/aoc_2017/day_4.gleam create mode 100644 gleam/aoc2017/src/aoc_2017/day_5.gleam create mode 100644 gleam/aoc2017/src/aoc_2017/day_6.gleam create mode 100644 gleam/aoc2017/src/aoc_2017/day_7.gleam create mode 100644 gleam/aoc2017/src/aoc_2017/day_8.gleam create mode 100644 gleam/aoc2017/src/aoc_2017/day_9.gleam create mode 100644 gleam/aoc2017/src/helpers/set_state.gleam create mode 100644 gleam/aoc2017/test/aoc2017_gleam_test.gleam create mode 100644 gleam/aoc2019/.github/workflows/test.yml create mode 100644 gleam/aoc2019/.gitignore create mode 100644 gleam/aoc2019/README.md create mode 100644 gleam/aoc2019/gleam.toml create mode 100644 gleam/aoc2019/manifest.toml create mode 100644 gleam/aoc2019/src/aoc2019_gleam.gleam create mode 100644 gleam/aoc2019/src/aoc_2019/day_1.gleam create mode 100644 gleam/aoc2019/src/aoc_2019/day_2.gleam create mode 100644 gleam/aoc2019/test/aoc2019_gleam_test.gleam create mode 100644 gleam/aoc2023/.DS_Store create mode 100644 gleam/aoc2023/.github/workflows/test.yml create mode 100644 gleam/aoc2023/.gitignore create mode 100644 gleam/aoc2023/README.md create mode 100644 gleam/aoc2023/gleam.toml create mode 100644 gleam/aoc2023/manifest.toml create mode 100644 gleam/aoc2023/src/.gitignore create mode 100644 gleam/aoc2023/src/day1/.gitignore create mode 100644 gleam/aoc2023/src/day1/solve.gleam create mode 100644 gleam/aoc2023/src/day10/.gitignore create mode 100644 gleam/aoc2023/src/day10/solve.gleam create mode 100644 gleam/aoc2023/src/day11/.gitignore create mode 100644 gleam/aoc2023/src/day11/solve.gleam create mode 100644 gleam/aoc2023/src/day12/.gitignore create mode 100644 gleam/aoc2023/src/day12/solve.gleam create mode 100644 gleam/aoc2023/src/day13/.gitignore create mode 100644 gleam/aoc2023/src/day13/solve.gleam create mode 100644 gleam/aoc2023/src/day14/.gitignore create mode 100644 gleam/aoc2023/src/day14/solve.gleam create mode 100644 gleam/aoc2023/src/day15/.gitignore create mode 100644 gleam/aoc2023/src/day15/solve.gleam create mode 100644 gleam/aoc2023/src/day16/.gitignore create mode 100644 gleam/aoc2023/src/day16/solve.gleam create mode 100644 gleam/aoc2023/src/day17/.gitignore create mode 100644 gleam/aoc2023/src/day17/solve.gleam create mode 100644 gleam/aoc2023/src/day18/.gitignore create mode 100644 gleam/aoc2023/src/day18/solve.gleam create mode 100644 gleam/aoc2023/src/day19/.gitignore create mode 100644 gleam/aoc2023/src/day19/solve.gleam create mode 100644 gleam/aoc2023/src/day2/.gitignore create mode 100644 gleam/aoc2023/src/day2/solve.gleam create mode 100644 gleam/aoc2023/src/day20/.gitignore create mode 100644 gleam/aoc2023/src/day20/solve.gleam create mode 100644 gleam/aoc2023/src/day21/.gitignore create mode 100644 gleam/aoc2023/src/day21/solve.gleam create mode 100644 gleam/aoc2023/src/day22/.gitignore create mode 100644 gleam/aoc2023/src/day22/solve.gleam create mode 100644 gleam/aoc2023/src/day23/.gitignore create mode 100644 gleam/aoc2023/src/day23/solve.gleam create mode 100644 gleam/aoc2023/src/day3/.gitignore create mode 100644 gleam/aoc2023/src/day3/solve.gleam create mode 100644 gleam/aoc2023/src/day4/.gitignore create mode 100644 gleam/aoc2023/src/day4/solve.gleam create mode 100644 gleam/aoc2023/src/day5/.gitignore create mode 100644 gleam/aoc2023/src/day5/solve.gleam create mode 100644 gleam/aoc2023/src/day6/.gitignore create mode 100644 gleam/aoc2023/src/day6/solve.gleam create mode 100644 gleam/aoc2023/src/day7/.gitignore create mode 100644 gleam/aoc2023/src/day7/solve.gleam create mode 100644 gleam/aoc2023/src/day8/.gitignore create mode 100644 gleam/aoc2023/src/day8/solve.gleam create mode 100644 gleam/aoc2023/src/day9/.gitignore create mode 100644 gleam/aoc2023/src/day9/solve.gleam create mode 100644 gleam/aoc2023/src/utilities/array2d.gleam create mode 100644 gleam/aoc2023/src/utilities/memo.gleam create mode 100644 gleam/aoc2023/src/utilities/prioqueue.gleam create mode 100644 gleam/aoc2023/test/aoc2023_test.gleam create mode 100644 gleam/aoc2023/test/day1/day1_test.gleam create mode 100644 gleam/aoc2023/test/day10/day10_test.gleam create mode 100644 gleam/aoc2023/test/day11/day11_test.gleam create mode 100644 gleam/aoc2023/test/day12/day12_test.gleam create mode 100644 gleam/aoc2023/test/day13/day13_test.gleam create mode 100644 gleam/aoc2023/test/day14/day14_test.gleam create mode 100644 gleam/aoc2023/test/day15/day15_test.gleam create mode 100644 gleam/aoc2023/test/day16/day16_test.gleam create mode 100644 gleam/aoc2023/test/day17/day17_test.gleam create mode 100644 gleam/aoc2023/test/day18/day18_test.gleam create mode 100644 gleam/aoc2023/test/day19/day19_test.gleam create mode 100644 gleam/aoc2023/test/day2/day2_test.gleam create mode 100644 gleam/aoc2023/test/day20/day20_test.gleam create mode 100644 gleam/aoc2023/test/day21/day21_test.gleam create mode 100644 gleam/aoc2023/test/day22/day22_test.gleam create mode 100644 gleam/aoc2023/test/day23/day23_test.gleam create mode 100644 gleam/aoc2023/test/day3/day3_test.gleam create mode 100644 gleam/aoc2023/test/day4/day4_test.gleam create mode 100644 gleam/aoc2023/test/day5/day5_test.gleam create mode 100644 gleam/aoc2023/test/day6/day6_test.gleam create mode 100644 gleam/aoc2023/test/day7/day7_test.gleam create mode 100644 gleam/aoc2023/test/day8/day8_test.gleam create mode 100644 gleam/aoc2023/test/day9/day9_test.gleam create mode 100644 gleam/codingquest2024/.github/workflows/test.yml create mode 100644 gleam/codingquest2024/.gitignore create mode 100644 gleam/codingquest2024/README.md create mode 100644 gleam/codingquest2024/gleam.toml create mode 100644 gleam/codingquest2024/manifest.toml create mode 100644 gleam/codingquest2024/src/codingquest2024.gleam create mode 100644 gleam/codingquest2024/src/day1/solution.gleam create mode 100644 gleam/codingquest2024/src/day2/solution.gleam create mode 100644 gleam/codingquest2024/src/day3/solution.gleam create mode 100644 gleam/codingquest2024/src/day4/solution.gleam create mode 100644 gleam/codingquest2024/src/day5/solution.gleam create mode 100644 gleam/codingquest2024/src/day6/solution.gleam create mode 100644 gleam/codingquest2024/src/day7/solution.gleam create mode 100644 gleam/codingquest2024/src/day8/solution.gleam create mode 100644 gleam/codingquest2024/src/day9/input.txt create mode 100644 gleam/codingquest2024/src/day9/solution.gleam create mode 100644 gleam/codingquest2024/src/utilities/memo.gleam create mode 100644 gleam/codingquest2024/test/codingquest2024_test.gleam delete mode 100644 leetcode/lc-1018-binary-prefix.rkt delete mode 100644 leetcode/lc-1037-boomerang.rkt delete mode 100644 leetcode/lc-1185-day-of-week.rkt delete mode 100644 leetcode/lc-1207-unique-occurences.rkt delete mode 100644 leetcode/lc-1221-split-a-string-balanced.rkt delete mode 100644 leetcode/lc-125-valid-palindrome.rkt delete mode 100644 leetcode/lc-1295-even-number-of-digits.rkt delete mode 100644 leetcode/lc-1299-replace-with-greatest-to-right.rkt delete mode 100644 leetcode/lc-1304-find-n-unique-integers.rkt delete mode 100644 leetcode/lc-1436-destination-city.rkt delete mode 100644 leetcode/lc-1450-students-doing-homework.rkt delete mode 100644 leetcode/lc-1460-make-two-arrays-equal.rkt delete mode 100644 leetcode/lc-1496-path-crossing.rkt delete mode 100644 leetcode/lc-1700-students-unable-to-eat.rkt delete mode 100644 leetcode/lc-1812-chessboard-square.rkt delete mode 100644 leetcode/lc-1844-replace-all-digits-with-characters.rkt delete mode 100644 leetcode/lc-1854-max-pop-year.rkt delete mode 100644 leetcode/lc-2-add-two-numbers.rkt delete mode 100644 leetcode/lc-217-contains-duplicate.rkt delete mode 100644 leetcode/lc-228-summary-ranges.rkt delete mode 100644 leetcode/lc-290-word-pattern.rkt delete mode 100644 leetcode/lc-345-reverse-vowels.rkt delete mode 100644 leetcode/lc-349-intersection-of-2-arrays.rkt delete mode 100644 leetcode/lc-36-valid-sudoku.rkt delete mode 100644 leetcode/lc-415-add-strings.rkt delete mode 100644 leetcode/lc-43-multiply-strings.rkt delete mode 100644 leetcode/lc-476-number-complement.rkt delete mode 100644 leetcode/lc-500-keyboard-row.rkt delete mode 100644 leetcode/lc-504-base7.rkt delete mode 100644 leetcode/lc-520-detect-capital.rkt delete mode 100644 leetcode/lc-551-student-attendance-record-1.rkt delete mode 100644 leetcode/lc-58-length-of-last-word.rkt delete mode 100644 leetcode/lc-645-set-mismatch.rkt delete mode 100644 leetcode/lc-657-robot-return.rkt delete mode 100644 leetcode/lc-68-justification.rkt delete mode 100644 leetcode/lc-690-employee-importance.rkt delete mode 100644 leetcode/lc-717-1bit-and-2bit.rkt delete mode 100644 leetcode/lc-745-prefix-suffix.rkt delete mode 100644 leetcode/lc-747-largest-number-twice.rkt delete mode 100644 leetcode/lc-766-toeplitz-matrix.rkt delete mode 100644 leetcode/lc-771-jewels-and-stones.rkt delete mode 100644 leetcode/lc-788-rotated-digits.rkt delete mode 100644 leetcode/lc-796-rotate-string.rkt delete mode 100644 leetcode/lc-819-most-common-word.rkt delete mode 100644 leetcode/lc-836-rectangle-overlap.rkt delete mode 100644 leetcode/lc-844-backspace-string-compare.rkt delete mode 100644 leetcode/lc-896-monotonic-array.rkt delete mode 100644 leetcode/lc-9-palindromic-number.rkt delete mode 100644 leetcode/lc-905-sort-by-parity.rkt delete mode 100644 leetcode/lc-944-delete-columns.rkt delete mode 100644 leetcode/lc-953-alien-dictionary.rkt delete mode 100644 leetcode/lc-989-add-to-array-form.rkt delete mode 100644 leetcode/lc-999-available-captures.rkt create mode 100644 racket/aoc2015/day-01/day-01.rkt create mode 100644 racket/aoc2015/day-02/day-02.rkt create mode 100644 racket/aoc2015/day-03/day-03.rkt create mode 100644 racket/aoc2015/day-04/day-04.rkt create mode 100644 racket/aoc2015/day-05/day-05.rkt create mode 100644 racket/aoc2015/day-06/day-06.rkt create mode 100644 racket/aoc2015/day-25/day-25.rkt create mode 100644 racket/aoc2018/day-01/day-01.rkt create mode 100644 racket/aoc2018/day-02/day-02.rkt create mode 100644 racket/aoc2018/day-03/day-03.rkt create mode 100644 racket/aoc2018/day-04/day-04.rkt create mode 100644 racket/aoc2018/day-05/day-05.rkt create mode 100644 racket/aoc2018/day-06/day-06.rkt create mode 100644 racket/aoc2019/day-02/day-02.rkt create mode 100644 racket/aoc2019/day-03/day-03.rkt create mode 100644 racket/aoc2019/day-04/day-04.rkt create mode 100644 racket/aoc2019/day-05/day-05.rkt create mode 100644 racket/aoc2020/day-01/day-01.rkt create mode 100644 racket/aoc2020/day-02/day-02.rkt create mode 100644 racket/aoc2020/day-03/day-03.rkt create mode 100644 racket/aoc2020/day-04/day-04.rkt create mode 100644 racket/aoc2020/day-05/day-05.rkt create mode 100644 racket/aoc2020/day-06/day-06.rkt create mode 100644 racket/aoc2020/day-07/day-07.rkt create mode 100644 racket/aoc2020/day-08/day-08.ipynb create mode 100644 racket/aoc2020/day-09/day-09.ipynb create mode 100644 racket/aoc2020/day-10/day-10.rkt create mode 100644 racket/aoc2020/day-11/day-11.rkt create mode 100644 racket/aoc2020/day-12/day-12.rkt create mode 100644 racket/aoc2020/day-13/day-13.rkt create mode 100644 racket/aoc2020/day-14/day-14.rkt create mode 100644 racket/aoc2020/day-15/day-15.rkt create mode 100644 racket/aoc2020/day-16/day-16.rkt create mode 100644 racket/aoc2021/day-01/day-01.pl create mode 100644 racket/aoc2021/day-01/day-01.rkt create mode 100644 racket/aoc2021/day-02/day-02.ex create mode 100644 racket/aoc2021/day-02/day-02.rkt create mode 100644 racket/aoc2021/day-03/day-03.rkt create mode 100644 racket/aoc2021/day-04/day-04.rkt create mode 100644 racket/aoc2021/day-05/day-05.rkt create mode 100644 racket/aoc2021/day-06/day-06.ex create mode 100644 racket/aoc2021/day-06/day-06.livemd create mode 100644 racket/aoc2021/day-06/day-06.rkt create mode 100644 racket/aoc2021/day-06/input.txt create mode 100644 racket/aoc2021/day-07/day-07.rkt create mode 100644 racket/aoc2021/day-08/day-08.rkt create mode 100644 racket/aoc2021/day-09/day-09.livemd create mode 100644 racket/aoc2021/day-09/day-09.rkt create mode 100644 racket/aoc2021/day-09/input.txt create mode 100644 racket/aoc2021/day-10/day-10.rkt create mode 100644 racket/aoc2021/day-11/day-11.rkt create mode 100644 racket/aoc2021/day-12/day-12.rkt create mode 100644 racket/aoc2021/day-13/day-13.rkt create mode 100644 racket/aoc2021/day-14/day-14.rkt create mode 100644 racket/aoc2021/day-15/day-15-list-nodes.rkt create mode 100644 racket/aoc2021/day-15/day-15.livemd create mode 100644 racket/aoc2021/day-15/day-15.rkt create mode 100644 racket/aoc2021/day-16/day-16.rkt create mode 100644 racket/aoc2021/day-17/day-17.rkt create mode 100644 racket/aoc2021/day-18/day-18.rkt create mode 100644 racket/aoc2021/day-19/day-19.rkt create mode 100644 racket/aoc2021/day-19/test-scanners create mode 100644 racket/aoc2021/day-20/day-20.rkt create mode 100644 racket/aoc2021/day-21/day-21.rkt create mode 100644 racket/aoc2021/day-22/day-22.rkt create mode 100644 racket/aoc2021/day-25/day-25.rkt create mode 100644 racket/aoc2022/commentary.md create mode 100644 racket/aoc2022/day-01/day-01.ipynb create mode 100644 racket/aoc2022/day-01/day-01.rkt create mode 100644 racket/aoc2022/day-02/day-02.ipynb create mode 100644 racket/aoc2022/day-02/day-02.pl create mode 100644 racket/aoc2022/day-02/prolog-input.txt create mode 100644 racket/aoc2022/day-03/day-03.ipynb create mode 100644 racket/aoc2022/day-04/day-04.ipynb create mode 100644 racket/aoc2022/day-05/day-05.ipynb create mode 100644 racket/aoc2022/day-05/day-05.rkt create mode 100644 racket/aoc2022/day-06/day-06.ipynb create mode 100644 racket/aoc2022/day-06/day-06.rkt create mode 100644 racket/aoc2022/day-07/day-07.rkt create mode 100644 racket/aoc2022/day-08/day-08.ipynb create mode 100644 racket/aoc2022/day-08/day-08.rkt create mode 100644 racket/aoc2022/day-09/day-09.rkt create mode 100644 racket/aoc2022/day-10/day-10.rkt create mode 100644 racket/aoc2022/day-11/day-11.rkt create mode 100644 racket/aoc2022/day-12/day-12.rkt create mode 100644 racket/aoc2022/day-13/day-13.rkt create mode 100644 racket/aoc2022/day-14/day-14.rkt create mode 100644 racket/aoc2022/day-15/day-15.rkt create mode 100644 racket/aoc2022/day-16/day-16.rkt create mode 100644 racket/aoc2022/day-17/day-17.rkt create mode 100644 racket/aoc2022/day-17/rock-shapes create mode 100644 racket/aoc2022/day-18/day-18.rkt create mode 100644 racket/aoc2022/day-19/day-19.rkt create mode 100644 racket/aoc2022/day-20/day-20.rkt create mode 100644 racket/aoc2022/day-21/day-21.rkt create mode 100644 racket/aoc2022/day-22/day-22.rkt create mode 100644 racket/aoc2022/day-23/day-23.rkt create mode 100644 racket/aoc2022/day-25/day-25.rkt create mode 100644 racket/aoc2023/day-01/day-01.rkt create mode 100644 racket/aoc2023/day-02/day-02-parser.rkt create mode 100644 racket/aoc2023/day-02/day-02.rkt create mode 100644 racket/aoc2023/day-03/day-03.rkt create mode 100644 racket/aoc2023/day-04/day-04.rkt create mode 100644 racket/aoc2023/day-05/day-05.rkt create mode 100644 racket/aoc2023/day-06/day-06.rkt create mode 100644 racket/aoc2023/day-07/day-07.rkt create mode 100644 racket/aoc2023/day-08/day-08.rkt create mode 100644 racket/aoc2023/day-09/day-09-polynomial.rkt create mode 100644 racket/aoc2023/day-09/day-09.rkt create mode 100644 racket/aoc2023/day-10/day-10.rkt create mode 100644 racket/aoc2023/day-11/day-11.rkt create mode 100644 racket/aoc2023/day-12/day-12.rkt create mode 100644 racket/aoc2023/day-13/day-13.rkt create mode 100644 racket/aoc2023/day-14/day-14.rkt create mode 100644 racket/aoc2023/day-15/day-15.rkt create mode 100644 racket/aoc2023/day-16/day-16.rkt create mode 100644 racket/aoc2023/day-17/day-17.rkt create mode 100644 racket/aoc2023/day-18/day-18.rkt create mode 100644 racket/aoc2023/day-19/day-19.rkt create mode 100644 racket/aoc2023/day-20/day-20.rkt create mode 100644 racket/aoc2023/day-21/day-21.rkt create mode 100644 racket/aoc2023/day-22/day-22.rkt create mode 100644 racket/aoc2023/day-23/day-23.rkt create mode 100644 racket/aoc2023/day-24/day-24a.rkt create mode 100644 racket/aoc2023/day-24/day-24b.rkt create mode 100644 racket/aoc2023/day-25/day-25.rkt create mode 100644 racket/leetcode/lc-1018-binary-prefix.rkt create mode 100644 racket/leetcode/lc-1037-boomerang.rkt create mode 100644 racket/leetcode/lc-1185-day-of-week.rkt create mode 100644 racket/leetcode/lc-1207-unique-occurences.rkt create mode 100644 racket/leetcode/lc-1221-split-a-string-balanced.rkt create mode 100644 racket/leetcode/lc-125-valid-palindrome.rkt create mode 100644 racket/leetcode/lc-1295-even-number-of-digits.rkt create mode 100644 racket/leetcode/lc-1299-replace-with-greatest-to-right.rkt create mode 100644 racket/leetcode/lc-1304-find-n-unique-integers.rkt create mode 100644 racket/leetcode/lc-1436-destination-city.rkt create mode 100644 racket/leetcode/lc-1450-students-doing-homework.rkt create mode 100644 racket/leetcode/lc-1460-make-two-arrays-equal.rkt create mode 100644 racket/leetcode/lc-1496-path-crossing.rkt create mode 100644 racket/leetcode/lc-1700-students-unable-to-eat.rkt create mode 100644 racket/leetcode/lc-1812-chessboard-square.rkt create mode 100644 racket/leetcode/lc-1844-replace-all-digits-with-characters.rkt create mode 100644 racket/leetcode/lc-1854-max-pop-year.rkt create mode 100644 racket/leetcode/lc-2-add-two-numbers.rkt create mode 100644 racket/leetcode/lc-217-contains-duplicate.rkt create mode 100644 racket/leetcode/lc-228-summary-ranges.rkt create mode 100644 racket/leetcode/lc-290-word-pattern.rkt create mode 100644 racket/leetcode/lc-345-reverse-vowels.rkt create mode 100644 racket/leetcode/lc-349-intersection-of-2-arrays.rkt create mode 100644 racket/leetcode/lc-36-valid-sudoku.rkt create mode 100644 racket/leetcode/lc-415-add-strings.rkt create mode 100644 racket/leetcode/lc-43-multiply-strings.rkt create mode 100644 racket/leetcode/lc-476-number-complement.rkt create mode 100644 racket/leetcode/lc-500-keyboard-row.rkt create mode 100644 racket/leetcode/lc-504-base7.rkt create mode 100644 racket/leetcode/lc-520-detect-capital.rkt create mode 100644 racket/leetcode/lc-551-student-attendance-record-1.rkt create mode 100644 racket/leetcode/lc-58-length-of-last-word.rkt create mode 100644 racket/leetcode/lc-645-set-mismatch.rkt create mode 100644 racket/leetcode/lc-657-robot-return.rkt create mode 100644 racket/leetcode/lc-68-justification.rkt create mode 100644 racket/leetcode/lc-690-employee-importance.rkt create mode 100644 racket/leetcode/lc-717-1bit-and-2bit.rkt create mode 100644 racket/leetcode/lc-745-prefix-suffix.rkt create mode 100644 racket/leetcode/lc-747-largest-number-twice.rkt create mode 100644 racket/leetcode/lc-766-toeplitz-matrix.rkt create mode 100644 racket/leetcode/lc-771-jewels-and-stones.rkt create mode 100644 racket/leetcode/lc-788-rotated-digits.rkt create mode 100644 racket/leetcode/lc-796-rotate-string.rkt create mode 100644 racket/leetcode/lc-819-most-common-word.rkt create mode 100644 racket/leetcode/lc-836-rectangle-overlap.rkt create mode 100644 racket/leetcode/lc-844-backspace-string-compare.rkt create mode 100644 racket/leetcode/lc-896-monotonic-array.rkt create mode 100644 racket/leetcode/lc-9-palindromic-number.rkt create mode 100644 racket/leetcode/lc-905-sort-by-parity.rkt create mode 100644 racket/leetcode/lc-944-delete-columns.rkt create mode 100644 racket/leetcode/lc-953-alien-dictionary.rkt create mode 100644 racket/leetcode/lc-989-add-to-array-form.rkt create mode 100644 racket/leetcode/lc-999-available-captures.rkt diff --git a/aoc2015/day-01/day-01.rkt b/aoc2015/day-01/day-01.rkt deleted file mode 100644 index efbd02a..0000000 --- a/aoc2015/day-01/day-01.rkt +++ /dev/null @@ -1,16 +0,0 @@ -#lang racket -(require "../../jj-aoc.rkt") - -;; part 1 -(for/fold ([current-floor 0]) ([l (in-input-port-chars (open-day 1 2015))] [i (in-naturals)]) - (match l - [#\( (add1 current-floor)] - [#\) (sub1 current-floor)])) - -;; part 2 -(for/fold ([current-floor 0] [last-index 0] #:result (add1 last-index)) - ([l (in-input-port-chars (open-day 1 2015))] [i (in-naturals)]) - #:break (= current-floor -1) - (match l - [#\( (values (add1 current-floor) i)] - [#\) (values (sub1 current-floor) i)])) diff --git a/aoc2015/day-02/day-02.rkt b/aoc2015/day-02/day-02.rkt deleted file mode 100644 index 579fd00..0000000 --- a/aoc2015/day-02/day-02.rkt +++ /dev/null @@ -1,26 +0,0 @@ -#lang racket -(require "../../jj-aoc.rkt" - threading - racket/struct) - -(struct present (l w h) #:transparent) - -(define presents - (for/list ([size-string (in-lines (open-day 2 2015))]) - (~> size-string (string-split "x") (map string->number _) (apply present _)))) - -;; part 1 -(define (paper-area p) - (define main-area (~> p struct->list (combinations 2) (map (λ~> (apply * 2 _)) _) (apply + _))) - (define slack-area (~> p struct->list (sort <) (take 2) (apply * _))) - (+ main-area slack-area)) - -(for/sum ([p (in-list presents)]) (paper-area p)) - -;; part 2 -(define (ribbon-length p) - (define ribbon-around-box (~> p struct->list (sort <) (take 2) (map (λ~> (* 2)) _) (apply + _))) - (define ribbon-for-bow (~> p struct->list (apply * _))) - (+ ribbon-around-box ribbon-for-bow)) - -(for/sum ([p (in-list presents)]) (ribbon-length p)) diff --git a/aoc2015/day-03/day-03.rkt b/aoc2015/day-03/day-03.rkt deleted file mode 100644 index 1d44955..0000000 --- a/aoc2015/day-03/day-03.rkt +++ /dev/null @@ -1,28 +0,0 @@ -#lang racket -(require "../../jj-aoc.rkt" - threading - (only-in algorithms chunks-of) - racket/hash) - -(define directions - (for/list ([l (in-input-port-chars (open-day 3 2015))]) - (string->symbol (string l)))) - -(define (trace-santa dirs) - (define visits (make-hash)) - (for/fold ([x 0] [y 0] #:result visits) ([dir (in-list dirs)]) - (hash-set! visits `(,x ,y) #true) - (match dir - ['^ (values x (add1 y))] - ['v (values x (sub1 y))] - ['< (values (add1 x) y)] - ['> (values (sub1 x) y)]))) - -;; part 1 -(~> directions trace-santa hash-values length) - -;; part 2 -(~> directions (chunks-of 2) (apply map list _) (map trace-santa _) (match-define (list real robo) _)) - -(hash-union! real robo #:combine (λ _ #true)) -(~> real hash-values length) diff --git a/aoc2015/day-04/day-04.rkt b/aoc2015/day-04/day-04.rkt deleted file mode 100644 index 2c16043..0000000 --- a/aoc2015/day-04/day-04.rkt +++ /dev/null @@ -1,18 +0,0 @@ -#lang racket -(require "../../jj-aoc.rkt" - threading - file/md5) - -(define secret-key (~> (open-day 4 2015) port->string string-trim)) - -(define (find-n-zeroes n) - (for/first ([i (in-naturals)] - #:when - (~>> i (~a secret-key) md5 bytes->string/utf-8 (string-prefix? _ (make-string n #\0)))) - i)) - -;; part 1 -(time (find-n-zeroes 5)) - -;; part 2 -(time (find-n-zeroes 6)) diff --git a/aoc2015/day-05/day-05.rkt b/aoc2015/day-05/day-05.rkt deleted file mode 100644 index 3449adc..0000000 --- a/aoc2015/day-05/day-05.rkt +++ /dev/null @@ -1,32 +0,0 @@ -#lang racket -(require "../../jj-aoc.rkt" - threading) - -(define strs (port->lines (open-day 5 2015))) - -;; part 1 -(define (at-least-three-vowels? str) - (~>> str (regexp-replace* #px"[^aeiou]" _ "") string-length (<= 3))) - -(define (at-least-one-pair? str) - (regexp-match? #px"(.)\\1{1,}" str)) - -(define (no-forbidden-pairs? str) - (~>> (list "ab" "cd" "pq" "xy") (ormap (λ~>> (string-contains? str))) not)) - -(define (nice? str) - (~>> (list at-least-three-vowels? at-least-one-pair? no-forbidden-pairs?) (andmap (λ (f) (f str))))) - -(count nice? strs) - -;; part 2 -(define (repeating-pair? str) - (regexp-match? #px"(..).*\\1" str)) - -(define (symmetry? str) - (regexp-match? #px"(.).\\1" str)) - -(define (new-nice? str) - (~>> (list repeating-pair? symmetry?) (andmap (λ (f) (f str))))) - -(count new-nice? strs) diff --git a/aoc2015/day-06/day-06.rkt b/aoc2015/day-06/day-06.rkt deleted file mode 100644 index d2eed08..0000000 --- a/aoc2015/day-06/day-06.rkt +++ /dev/null @@ -1,49 +0,0 @@ -#lang racket -(require "../../jj-aoc.rkt" - threading) - -(struct instruction (todo x1 y1 x2 y2) #:transparent) - -(define (make-instruction lst) - (apply instruction (string->symbol (first lst)) (map string->number (rest lst)))) - -(define instructions - (for/list ([l (in-lines (open-day 6 2015))]) - (~>> l - (regexp-match - #px"(turn on|toggle|turn off) (\\d{1,3}),(\\d{1,3}) through (\\d{1,3}),(\\d{1,3})") - rest - make-instruction))) - -(define (vector2d-modify! vec x y f) - (define pos (+ x (* 1000 y))) - (vector-set! vec pos (f (vector-ref vec pos)))) - -;; part 1 -(define (todo inst) - (match (instruction-todo inst) - ['|turn on| (λ _ #true)] - ['|turn off| (λ _ #false)] - ['|toggle| not])) - -(define (modify-light-grid inst light-grid using) - (for ([x (inclusive-range (instruction-x1 inst) (instruction-x2 inst))]) - (for ([y (inclusive-range (instruction-y1 inst) (instruction-y2 inst))]) - (vector2d-modify! light-grid x y (using inst))))) - -(define light-grid (make-vector (* 1000 1000) #false)) -(for ([i (in-list instructions)]) - (modify-light-grid i light-grid todo)) -(vector-count identity light-grid) - -;; part 2 -(define (todo-dimmer inst) - (match (instruction-todo inst) - ['|turn on| add1] - ['|turn off| (λ (x) (max 0 (sub1 x)))] - ['|toggle| (curry + 2)])) - -(define dimmable-grid (make-vector (* 1000 1000) 0)) -(for ([i (in-list instructions)]) - (modify-light-grid i dimmable-grid todo-dimmer)) -(apply + (vector->list dimmable-grid)) diff --git a/aoc2015/day-25/day-25.rkt b/aoc2015/day-25/day-25.rkt deleted file mode 100644 index 975f4c3..0000000 --- a/aoc2015/day-25/day-25.rkt +++ /dev/null @@ -1,8 +0,0 @@ -#lang racket - -(define max-r 2978) -(define max-c 3083) - -(for/fold ([code 20151125] [r 1] [c 1]) ([i (in-naturals)] #:break (and (= max-r r) (= max-c c))) - (define new-code (modulo (* code 252533) 33554393)) - (if (= r 1) (values new-code (add1 c) 1) (values new-code (sub1 r) (add1 c)))) diff --git a/aoc2017-gleam/.github/workflows/test.yml b/aoc2017-gleam/.github/workflows/test.yml deleted file mode 100644 index 664c44a..0000000 --- a/aoc2017-gleam/.github/workflows/test.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: test - -on: - push: - branches: - - master - - main - pull_request: - -jobs: - test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: erlef/setup-beam@v1 - with: - otp-version: "26.0.2" - gleam-version: "1.2.0-rc1" - rebar3-version: "3" - # elixir-version: "1.15.4" - - run: gleam deps download - - run: gleam test - - run: gleam format --check src test diff --git a/aoc2017-gleam/.gitignore b/aoc2017-gleam/.gitignore deleted file mode 100644 index 599be4e..0000000 --- a/aoc2017-gleam/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -*.beam -*.ez -/build -erl_crash.dump diff --git a/aoc2017-gleam/README.md b/aoc2017-gleam/README.md deleted file mode 100644 index 9714473..0000000 --- a/aoc2017-gleam/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# aoc2017_gleam - -[![Package Version](https://img.shields.io/hexpm/v/aoc2017_gleam)](https://hex.pm/packages/aoc2017_gleam) -[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/aoc2017_gleam/) - -```sh -gleam add aoc2017_gleam -``` -```gleam -import aoc2017_gleam - -pub fn main() { - // TODO: An example of the project in use -} -``` - -Further documentation can be found at . - -## Development - -```sh -gleam run # Run the project -gleam test # Run the tests -gleam shell # Run an Erlang shell -``` diff --git a/aoc2017-gleam/gleam.toml b/aoc2017-gleam/gleam.toml deleted file mode 100644 index e00659c..0000000 --- a/aoc2017-gleam/gleam.toml +++ /dev/null @@ -1,24 +0,0 @@ -name = "aoc2017_gleam" -version = "1.0.0" - -# Fill out these fields if you intend to generate HTML documentation or publish -# your project to the Hex package manager. -# -# description = "" -# licences = ["Apache-2.0"] -# repository = { type = "github", user = "username", repo = "project" } -# links = [{ title = "Website", href = "https://gleam.run" }] -# -# For a full reference of all the available options, you can have a look at -# https://gleam.run/writing-gleam/gleam-toml/. - -[dependencies] -gleam_stdlib = ">= 0.34.0 and < 2.0.0" -gladvent = ">= 0.7.3 and < 1.0.0" -gary = ">= 1.0.1 and < 2.0.0" -gleam_otp = ">= 0.10.0 and < 1.0.0" -gleam_erlang = ">= 0.25.0 and < 1.0.0" -glearray = ">= 0.2.2 and < 1.0.0" - -[dev-dependencies] -gleeunit = ">= 1.0.0 and < 2.0.0" diff --git a/aoc2017-gleam/manifest.toml b/aoc2017-gleam/manifest.toml deleted file mode 100644 index f48e595..0000000 --- a/aoc2017-gleam/manifest.toml +++ /dev/null @@ -1,36 +0,0 @@ -# This file was generated by Gleam -# You typically do not need to edit this file - -packages = [ - { name = "argv", version = "1.0.2", build_tools = ["gleam"], requirements = [], otp_app = "argv", source = "hex", outer_checksum = "BA1FF0929525DEBA1CE67256E5ADF77A7CDDFE729E3E3F57A5BDCAA031DED09D" }, - { name = "filepath", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "EFB6FF65C98B2A16378ABC3EE2B14124168C0CE5201553DE652E2644DCFDB594" }, - { name = "gary", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gary", source = "hex", outer_checksum = "4C05611EEC74876A1E36309EED22C826B92F22E566579ACBC65C579CD615EC60" }, - { name = "gladvent", version = "0.7.3", build_tools = ["gleam"], requirements = ["argv", "filepath", "gleam_erlang", "gleam_json", "gleam_otp", "gleam_package_interface", "gleam_stdlib", "glint", "parallel_map", "shellout", "simplifile", "snag", "spinner", "tom"], otp_app = "gladvent", source = "hex", outer_checksum = "59D93FD759427BCE8EE9828C0B62A3CD18362225F948C9789D334DCEAD621358" }, - { name = "gleam_community_ansi", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_community_colour", "gleam_stdlib"], otp_app = "gleam_community_ansi", source = "hex", outer_checksum = "FE79E08BF97009729259B6357EC058315B6FBB916FAD1C2FF9355115FEB0D3A4" }, - { name = "gleam_community_colour", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib"], otp_app = "gleam_community_colour", source = "hex", outer_checksum = "795964217EBEDB3DA656F5EB8F67D7AD22872EB95182042D3E7AFEF32D3FD2FE" }, - { name = "gleam_erlang", version = "0.25.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "054D571A7092D2A9727B3E5D183B7507DAB0DA41556EC9133606F09C15497373" }, - { name = "gleam_json", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "thoas"], otp_app = "gleam_json", source = "hex", outer_checksum = "9063D14D25406326C0255BDA0021541E797D8A7A12573D849462CAFED459F6EB" }, - { name = "gleam_otp", version = "0.10.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "0B04FE915ACECE539B317F9652CAADBBC0F000184D586AAAF2D94C100945D72B" }, - { name = "gleam_package_interface", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib"], otp_app = "gleam_package_interface", source = "hex", outer_checksum = "52A721BCA972C8099BB881195D821AAA64B9F2655BECC102165D5A1097731F01" }, - { name = "gleam_stdlib", version = "0.38.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "663CF11861179AF415A625307447775C09404E752FF99A24E2057C835319F1BE" }, - { name = "glearray", version = "0.2.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "glearray", source = "hex", outer_checksum = "9C207E05F38D724F464FA921378DB3ABC2B0A2F5821116D8BC8B2CACC68930D5" }, - { name = "gleeunit", version = "1.1.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "72CDC3D3F719478F26C4E2C5FED3E657AC81EC14A47D2D2DEBB8693CA3220C3B" }, - { name = "glint", version = "1.0.0-rc2", build_tools = ["gleam"], requirements = ["gleam_community_ansi", "gleam_community_colour", "gleam_stdlib", "snag"], otp_app = "glint", source = "hex", outer_checksum = "FD5C47CE237CA67121F3946ADE7C630750BB67F5E8A4717D2DF5B5EE758CCFDB" }, - { name = "parallel_map", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_otp", "gleam_stdlib"], otp_app = "parallel_map", source = "hex", outer_checksum = "653714A9FD63EACD1A9D0A6582A972B0EC109AE275CDDD2E99CFC3DFAFAB9225" }, - { name = "repeatedly", version = "2.1.1", build_tools = ["gleam"], requirements = [], otp_app = "repeatedly", source = "hex", outer_checksum = "38808C3EC382B0CD981336D5879C24ECB37FCB9C1D1BD128F7A80B0F74404D79" }, - { name = "shellout", version = "1.6.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "shellout", source = "hex", outer_checksum = "E2FCD18957F0E9F67E1F497FC9FF57393392F8A9BAEAEA4779541DE7A68DD7E0" }, - { name = "simplifile", version = "1.7.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "1D5DFA3A2F9319EC85825F6ED88B8E449F381B0D55A62F5E61424E748E7DDEB0" }, - { name = "snag", version = "0.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "snag", source = "hex", outer_checksum = "54D32E16E33655346AA3E66CBA7E191DE0A8793D2C05284E3EFB90AD2CE92BCC" }, - { name = "spinner", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_community_ansi", "gleam_erlang", "gleam_stdlib", "glearray", "repeatedly"], otp_app = "spinner", source = "hex", outer_checksum = "200BA3D4A04D468898E63C0D316E23F526E02514BC46454091975CB5BAE41E8F" }, - { name = "thoas", version = "1.2.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "E38697EDFFD6E91BD12CEA41B155115282630075C2A727E7A6B2947F5408B86A" }, - { name = "tom", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "tom", source = "hex", outer_checksum = "A5364613E3DBF77F38EFF81DA9F99324086D029EC2B2D44348762FBE38602311" }, -] - -[requirements] -gary = { version = ">= 1.0.1 and < 2.0.0" } -gladvent = { version = ">= 0.7.3 and < 1.0.0" } -gleam_erlang = { version = ">= 0.25.0 and < 1.0.0" } -gleam_otp = { version = ">= 0.10.0 and < 1.0.0" } -gleam_stdlib = { version = ">= 0.34.0 and < 2.0.0" } -glearray = { version = ">= 0.2.2 and < 1.0.0"} -gleeunit = { version = ">= 1.0.0 and < 2.0.0" } diff --git a/aoc2017-gleam/src/aoc2017_gleam.gleam b/aoc2017-gleam/src/aoc2017_gleam.gleam deleted file mode 100644 index 8c2ba67..0000000 --- a/aoc2017-gleam/src/aoc2017_gleam.gleam +++ /dev/null @@ -1,5 +0,0 @@ -import gleam/io - -pub fn main() { - io.println("Hello from aoc2017_gleam!") -} diff --git a/aoc2017-gleam/src/aoc_2017/day_1.gleam b/aoc2017-gleam/src/aoc_2017/day_1.gleam deleted file mode 100644 index 786d0dd..0000000 --- a/aoc2017-gleam/src/aoc_2017/day_1.gleam +++ /dev/null @@ -1,32 +0,0 @@ -import gleam/int -import gleam/list -import gleam/result -import gleam/string - -pub fn parse(input: String) { - input - |> string.to_graphemes() - |> list.map(int.parse) - |> result.values() -} - -pub fn pt_1(input: List(Int)) { - pair_by(numbers: input, considering: 1) -} - -pub fn pt_2(input: List(Int)) { - pair_by(numbers: input, considering: list.length(input) / 2) -} - -fn find_neighbor_matches(number_pairs: List(#(Int, Int))) { - case number_pairs { - [] -> 0 - [#(a, b), ..rest] if a == b -> a + find_neighbor_matches(rest) - [_, ..rest] -> find_neighbor_matches(rest) - } -} - -fn pair_by(numbers xs: List(Int), considering by: Int) { - list.zip(xs, list.append(list.drop(xs, by), list.take(xs, by))) - |> find_neighbor_matches() -} diff --git a/aoc2017-gleam/src/aoc_2017/day_10.gleam b/aoc2017-gleam/src/aoc_2017/day_10.gleam deleted file mode 100644 index 676e0ee..0000000 --- a/aoc2017-gleam/src/aoc_2017/day_10.gleam +++ /dev/null @@ -1,77 +0,0 @@ -import gleam/int -import gleam/list -import gleam/result -import gleam/string - -const size = 256 - -const suffix = [17, 31, 73, 47, 23] - -fn parse_as_numbers(input: String) { - input - |> string.split(",") - |> list.map(int.parse) - |> result.values() -} - -fn parse_as_bytes(input: String) { - input - |> string.to_utf_codepoints - |> list.map(string.utf_codepoint_to_int) - |> list.append(suffix) -} - -pub fn pt_1(input: String) { - let twisted = twist(list.range(0, size - 1), parse_as_numbers(input), 0, 0) - - let assert #([first, second, ..], _, _) = twisted - first * second -} - -pub fn pt_2(input: String) { - megatwist(list.range(0, size - 1), parse_as_bytes(input), 0, 0, 64) - |> list.sized_chunk(16) - |> list.map(fold_xor) - |> string.concat() -} - -fn twist(loop: List(Int), lengths: List(Int), skip: Int, index: Int) { - case lengths { - [] -> #(loop, skip, index) - [l, ..ls] -> - loop - |> roll(index) - |> flip(l) - |> roll({ size - index } % size) - |> twist(ls, skip + 1, { index + l + skip } % size) - } -} - -fn megatwist(loop, lengths, skip, index, iterations) { - case iterations { - 0 -> loop - n -> { - let #(next_loop, next_skip, next_index) = - twist(loop, lengths, skip, index) - megatwist(next_loop, lengths, next_skip, next_index, n - 1) - } - } -} - -fn roll(list: List(a), by: Int) { - let #(left, right) = list.split(list, by % size) - list.append(right, left) -} - -fn flip(list: List(a), length: Int) { - let #(left, right) = list.split(list, length) - list.append(list.reverse(left), right) -} - -fn fold_xor(xs: List(Int)) { - let assert Ok(n) = list.reduce(xs, int.bitwise_exclusive_or) - n - |> int.to_base16() - |> string.pad_left(to: 2, with: "0") - |> string.lowercase() -} diff --git a/aoc2017-gleam/src/aoc_2017/day_11.gleam b/aoc2017-gleam/src/aoc_2017/day_11.gleam deleted file mode 100644 index 7d3df0b..0000000 --- a/aoc2017-gleam/src/aoc_2017/day_11.gleam +++ /dev/null @@ -1,75 +0,0 @@ -import gleam/int -import gleam/list -import gleam/string - -pub type Direction { - North - Northeast - Northwest - South - Southeast - Southwest -} - -type HexPosition { - HexPosition(x: Int, y: Int, z: Int) -} - -const start = HexPosition(0, 0, 0) - -fn to_direction(str: String) -> Direction { - case str { - "n" -> North - "ne" -> Northeast - "nw" -> Northwest - "s" -> South - "se" -> Southeast - "sw" -> Southwest - _ -> panic as "unrecognized direction" - } -} - -fn distance(hp1: HexPosition, hp2: HexPosition) -> Int { - { - int.absolute_value(hp1.x - hp2.x) - + int.absolute_value(hp1.y - hp2.y) - + int.absolute_value(hp1.z - hp2.z) - } - / 2 -} - -fn move(p, direction) -> HexPosition { - case direction { - North -> HexPosition(..p, y: p.y + 1, z: p.z - 1) - South -> HexPosition(..p, y: p.y - 1, z: p.z + 1) - Northeast -> HexPosition(..p, x: p.x + 1, z: p.z - 1) - Southwest -> HexPosition(..p, x: p.x - 1, z: p.z + 1) - Southeast -> HexPosition(..p, x: p.x + 1, y: p.y - 1) - Northwest -> HexPosition(..p, x: p.x - 1, y: p.y + 1) - } -} - -pub fn parse(input: String) -> List(Direction) { - input - |> string.split(",") - |> list.map(to_direction) -} - -pub fn pt_1(input: List(Direction)) { - do_walk(input, start, 0).0 -} - -pub fn pt_2(input: List(Direction)) { - do_walk(input, start, 0).1 -} - -fn do_walk(steps, position, max) { - case steps { - [] -> #(distance(position, HexPosition(0, 0, 0)), max) - [next, ..rest] -> { - let new_position = move(position, next) - let new_max = int.max(max, distance(new_position, start)) - do_walk(rest, new_position, new_max) - } - } -} diff --git a/aoc2017-gleam/src/aoc_2017/day_12.gleam b/aoc2017-gleam/src/aoc_2017/day_12.gleam deleted file mode 100644 index a9d73c5..0000000 --- a/aoc2017-gleam/src/aoc_2017/day_12.gleam +++ /dev/null @@ -1,44 +0,0 @@ -import gleam/dict -import gleam/list -import gleam/set.{type Set} -import gleam/string - -type Pipes = - dict.Dict(String, List(String)) - -pub fn parse(input: String) -> Pipes { - use acc, row <- list.fold(string.split(input, "\n"), dict.new()) - let assert Ok(#(from, to)) = string.split_once(row, " <-> ") - let to_nodes = string.split(to, ", ") - dict.insert(acc, from, to_nodes) -} - -pub fn pt_1(input: Pipes) { - next_nodes("0", input, set.new()) |> set.size() -} - -pub fn pt_2(input: Pipes) { - count_groups(dict.keys(input), input, 0) -} - -fn next_nodes(current: String, pipes: Pipes, found: Set(String)) { - let assert Ok(to_nodes) = dict.get(pipes, current) - - use acc, node <- list.fold(to_nodes, found) - case set.contains(found, node) { - False -> acc |> set.insert(node) |> next_nodes(node, pipes, _) - True -> acc - } -} - -fn count_groups(all_nodes: List(String), pipes: Pipes, count: Int) { - case all_nodes { - [] -> count - [first, ..] -> { - let next_subgraph = next_nodes(first, pipes, set.new()) - let remaining = - list.filter(all_nodes, fn(n) { !set.contains(next_subgraph, n) }) - count_groups(remaining, pipes, count + 1) - } - } -} diff --git a/aoc2017-gleam/src/aoc_2017/day_13.gleam b/aoc2017-gleam/src/aoc_2017/day_13.gleam deleted file mode 100644 index 6b04a77..0000000 --- a/aoc2017-gleam/src/aoc_2017/day_13.gleam +++ /dev/null @@ -1,58 +0,0 @@ -import gleam/int -import gleam/list -import gleam/option.{Some} -import gleam/regex.{Match} -import gleam/string - -pub type Layer { - Layer(depth: Int, cycle: Int) -} - -pub type Status { - Caught(severity: Int) - Free -} - -pub fn parse(input: String) { - let assert Ok(re) = regex.from_string("([0-9]+): ([0-9]+)") - - use acc, row <- list.fold(string.split(input, "\n"), []) - let assert [Match(submatches: [Some(depth), Some(cycle)], ..)] = - regex.scan(row, with: re) - let assert Ok(depth) = int.parse(depth) - let assert Ok(cycle) = int.parse(cycle) - [Layer(depth, cycle), ..acc] -} - -fn severity(time: Int, depth: Int, cycle: Int) { - case { time + depth } % { 2 * { cycle - 1 } } { - 0 -> Caught(cycle * depth) - _ -> Free - } -} - -pub fn pt_1(input: List(Layer)) { - use acc, layer <- list.fold(input, 0) - case severity(0, layer.depth, layer.cycle) { - Free -> acc - Caught(severity) -> acc + severity - } -} - -pub fn pt_2(input: List(Layer)) { - find_delay(0, input) -} - -fn find_delay(delay: Int, layers: List(Layer)) { - let trial_run = - list.try_fold(layers, Free, fn(_, layer) { - case severity(delay, layer.depth, layer.cycle) { - Free -> Ok(Free) - Caught(_) -> Error(Nil) - } - }) - case trial_run { - Ok(_) -> delay - _err -> find_delay(delay + 1, layers) - } -} diff --git a/aoc2017-gleam/src/aoc_2017/day_14.gleam b/aoc2017-gleam/src/aoc_2017/day_14.gleam deleted file mode 100644 index 2a74912..0000000 --- a/aoc2017-gleam/src/aoc_2017/day_14.gleam +++ /dev/null @@ -1,82 +0,0 @@ -import aoc_2017/day_10.{pt_2 as knot} -import gleam/int -import gleam/list -import gleam/result -import gleam/set -import gleam/string -import helpers/set_state - -pub fn pt_1(input: String) { - use acc, row <- list.fold(make_rows(input), 0) - let count = row |> knot() |> popcount() - acc + count -} - -fn make_rows(input: String) { - use row <- list.map(list.range(0, 127)) - input <> "-" <> int.to_string(row) -} - -fn popcount(hex_number: String) -> Int { - let assert Ok(n) = int.base_parse(hex_number, 16) - let assert Ok(digits) = int.digits(n, 2) - - use acc, digit <- list.fold(digits, 0) - case digit { - 1 -> acc + 1 - _ -> acc - } -} - -pub fn pt_2(input: String) { - let grid = set_state.start_actor(make_grid(input)) - - find_next_group(grid, 0) -} - -fn make_grid(input: String) { - let raw_grid = - list.map(make_rows(input), fn(row) { - row - |> knot() - |> int.base_parse(16) - |> result.map(int.to_base2) - |> result.map(string.pad_left(_, with: "0", to: 128)) - |> result.map(string.to_graphemes) - }) - |> result.values - - { - use total_acc, row, i <- list.index_fold(raw_grid, set.new()) - use acc, bit, j <- list.index_fold(row, total_acc) - case bit { - "1" -> set.insert(acc, #(i, j)) - _zero -> acc - } - } -} - -fn find_next_group(actor, count) { - case set_state.pop(actor) { - Ok(p) -> { - list.each(neighbors(p), remove_neighbor(actor, _)) - find_next_group(actor, count + 1) - } - Error(Nil) -> count - } -} - -fn neighbors(of: #(Int, Int)) { - let #(i, j) = of - [#(i + 1, j), #(i - 1, j), #(i, j + 1), #(i, j - 1)] -} - -fn remove_neighbor(actor, point) { - case set_state.check(actor, point) { - True -> { - set_state.drop(actor, point) - list.each(neighbors(point), remove_neighbor(actor, _)) - } - False -> Nil - } -} diff --git a/aoc2017-gleam/src/aoc_2017/day_15.gleam b/aoc2017-gleam/src/aoc_2017/day_15.gleam deleted file mode 100644 index dab5c14..0000000 --- a/aoc2017-gleam/src/aoc_2017/day_15.gleam +++ /dev/null @@ -1,93 +0,0 @@ -import gleam/int -import gleam/string - -const sixteen_bits = 0xFFFF - -const generator_a = 16_807 - -const generator_b = 48_271 - -const divisor = 2_147_483_647 - -const max_reps_pt1 = 40_000_000 - -const max_reps_pt2 = 5_000_000 - -pub fn parse(input: String) { - let assert Ok(#(a_str, b_str)) = string.split_once(input, ",") - let assert Ok(a) = int.parse(a_str) - let assert Ok(b) = int.parse(b_str) - - #(a, b) -} - -pub fn pt_1(input: #(Int, Int)) { - let #(a, b) = input - - next_comparison( - a: a, - b: b, - selecting_a_using: next_value, - selecting_b_using: next_value, - initial_matches: 0, - initial_cycle: 0, - up_to: max_reps_pt1, - ) -} - -pub fn pt_2(input: #(Int, Int)) { - let #(a, b) = input - - next_comparison( - a: a, - b: b, - selecting_a_using: next_but_divisible_by(4), - selecting_b_using: next_but_divisible_by(8), - initial_matches: 0, - initial_cycle: 0, - up_to: max_reps_pt2, - ) -} - -fn next_value(current, generator) { - { current * generator } % divisor -} - -fn picky_generator(current, generator, divisible_by) { - let trial = next_value(current, generator) - case trial % divisible_by { - 0 -> trial - _ -> picky_generator(trial, generator, divisible_by) - } -} - -fn next_but_divisible_by(divisible_by) { - fn(c, g) { picky_generator(c, g, divisible_by) } -} - -fn last_16_bits(n: Int) { - int.bitwise_and(n, sixteen_bits) -} - -fn next_comparison( - a a: Int, - b b: Int, - selecting_a_using for_a: fn(Int, Int) -> Int, - selecting_b_using for_b: fn(Int, Int) -> Int, - initial_matches same: Int, - initial_cycle count: Int, - up_to max: Int, -) { - case count == max { - True -> same - False -> { - let next_a = for_a(a, generator_a) - let next_b = for_b(b, generator_b) - let maybe_same = case last_16_bits(next_a) == last_16_bits(next_b) { - True -> same + 1 - False -> same - } - next_comparison(next_a, next_b, for_a, for_b, maybe_same, count + 1, max) - } - } -} diff --git a/aoc2017-gleam/src/aoc_2017/day_16.gleam b/aoc2017-gleam/src/aoc_2017/day_16.gleam deleted file mode 100644 index 9a5110e..0000000 --- a/aoc2017-gleam/src/aoc_2017/day_16.gleam +++ /dev/null @@ -1,104 +0,0 @@ -import gleam/dict.{type Dict} -import gleam/int -import gleam/list -import gleam/string - -pub type DanceMove { - Spin(moving: Int) - Exchange(first: Int, second: Int) - Partner(first: String, second: String) -} - -const initial_lineup = "abcdefghijklmnop" - -const dancer_count = 16 - -const end_state = 1_000_000_000 - -pub fn parse(input: String) { - string.split(input, ",") -} - -fn do_spin(dancers: List(String), moving: Int) { - let #(front, back) = list.split(dancers, dancer_count - moving) - list.append(back, front) -} - -fn do_exchange(dancers: List(String), first: Int, second: Int) { - let indexed = list.index_map(dancers, fn(d, i) { #(i, d) }) - - let assert Ok(first_dancer) = list.key_find(indexed, first) - let assert Ok(second_dancer) = list.key_find(indexed, second) - - indexed - |> list.key_set(first, second_dancer) - |> list.key_set(second, first_dancer) - |> list.map(fn(tup) { tup.1 }) -} - -fn do_partner(dancers: List(String), first: String, second: String) { - use dancer <- list.map(dancers) - case dancer { - d if d == first -> second - d if d == second -> first - d -> d - } -} - -pub fn pt_1(input: List(String)) { - initial_lineup - |> string.to_graphemes() - |> next_move(input) - |> string.concat() -} - -fn next_move(dancers, raw_moves) { - case raw_moves { - [] -> dancers - ["s" <> size, ..rest] -> dancers |> do_spin(int(size)) |> next_move(rest) - ["x" <> swap, ..rest] -> { - let assert Ok(#(first, second)) = string.split_once(swap, "/") - dancers |> do_exchange(int(first), int(second)) |> next_move(rest) - } - ["p" <> swap, ..rest] -> { - let assert Ok(#(first, second)) = string.split_once(swap, "/") - dancers |> do_partner(first, second) |> next_move(rest) - } - _ -> panic as "bad dance move" - } -} - -pub fn pt_2(input: List(String)) { - initial_lineup - |> string.to_graphemes() - |> find_cycle(caching_in: dict.new(), cycle: 0, dancing_to: input) -} - -fn find_cycle( - moving_to dance_position: List(String), - caching_in cache: Dict(String, Int), - cycle cycle: Int, - dancing_to dance_moves: List(String), -) { - let dance_hash = string.concat(dance_position) - case dict.get(cache, dance_hash) { - Ok(c) -> { - let offset = end_state % { cycle - c } - c - let assert [#(final, _)] = - dict.filter(cache, fn(_, v) { v == offset }) |> dict.to_list() - final - } - _err -> - find_cycle( - moving_to: next_move(dance_position, dance_moves), - caching_in: dict.insert(cache, dance_hash, cycle), - cycle: cycle + 1, - dancing_to: dance_moves, - ) - } -} - -fn int(n) { - let assert Ok(n) = int.parse(n) - n -} diff --git a/aoc2017-gleam/src/aoc_2017/day_17.gleam b/aoc2017-gleam/src/aoc_2017/day_17.gleam deleted file mode 100644 index 5904cab..0000000 --- a/aoc2017-gleam/src/aoc_2017/day_17.gleam +++ /dev/null @@ -1,55 +0,0 @@ -import gleam/int -import gleam/list -import glearray - -pub fn parse(input: String) { - let assert Ok(n) = int.parse(input) - n -} - -pub fn pt_1(input: Int) { - let assert [_, result] = - next_spin([0], 0, 1, input) - |> list.drop_while(fn(x) { x != 2017 }) - |> list.take(2) - - result -} - -fn next_spin(list: List(Int), position: Int, cycle: Int, step: Int) { - case cycle { - 2018 -> list - _ -> { - let next_position = { position + step } % cycle + 1 - next_spin( - insert_at(list, next_position, cycle), - next_position, - cycle + 1, - step, - ) - } - } -} - -fn insert_at(xs: List(a), at index: Int, insert new: a) { - let #(left, right) = list.split(xs, index) - list.concat([left, [new], right]) -} - -pub fn pt_2(input: Int) { - next_spin_tracking_zero(0, 0, 1, input) -} - -fn next_spin_tracking_zero(acc: Int, position: Int, cycle: Int, step: Int) { - case cycle { - 50_000_001 -> acc - _ -> { - let next_position = { position + step } % cycle + 1 - let next_acc = case next_position { - 1 -> cycle - _ -> acc - } - next_spin_tracking_zero(next_acc, next_position, cycle + 1, step) - } - } -} diff --git a/aoc2017-gleam/src/aoc_2017/day_2.gleam b/aoc2017-gleam/src/aoc_2017/day_2.gleam deleted file mode 100644 index 6a5e85d..0000000 --- a/aoc2017-gleam/src/aoc_2017/day_2.gleam +++ /dev/null @@ -1,47 +0,0 @@ -import gleam/int -import gleam/list -import gleam/result -import gleam/string - -pub fn parse(input: String) { - use row <- list.map(string.split(input, "\n")) - use val <- list.map(string.split(row, "\t")) - let assert Ok(n) = int.parse(val) - n -} - -pub fn pt_1(input: List(List(Int))) { - use acc, row <- list.fold(input, 0) - acc + max(row) - min(row) -} - -pub fn pt_2(input: List(List(Int))) { - use acc, row <- list.fold(input, 0) - let assert [val] = - row |> list.combination_pairs() |> list.map(test_pair) |> result.values() - acc + val -} - -fn max(xs) { - let assert Ok(result) = list.reduce(xs, int.max) - result -} - -fn min(xs) { - let assert Ok(result) = list.reduce(xs, int.min) - result -} - -fn test_pair(tup) { - case tup { - #(a, b) if a > b -> check_divisibility(a, b) - #(b, a) -> check_divisibility(a, b) - } -} - -fn check_divisibility(a, b) { - case a % b { - 0 -> Ok(a / b) - _ -> Error(Nil) - } -} diff --git a/aoc2017-gleam/src/aoc_2017/day_3.gleam b/aoc2017-gleam/src/aoc_2017/day_3.gleam deleted file mode 100644 index 5672e39..0000000 --- a/aoc2017-gleam/src/aoc_2017/day_3.gleam +++ /dev/null @@ -1,87 +0,0 @@ -import gleam/dict -import gleam/int -import gleam/list - -type Direction { - Up - Down - Left - Right -} - -type State { - State( - x: Int, - y: Int, - direction: Direction, - branch_length: Int, - remaining: Int, - ) -} - -fn starting_state() -> State { - State(0, 0, Right, 1, 1) -} - -fn update_state(state: State) -> State { - case state { - State(x, y, Right, len, 0) -> State(x, y + 1, Up, len, len - 1) - State(x, y, Up, len, 0) -> State(x - 1, y, Left, len + 1, len) - State(x, y, Left, len, 0) -> State(x, y - 1, Down, len, len - 1) - State(x, y, Down, len, 0) -> State(x + 1, y, Right, len + 1, len) - State(x, y, Right, len, rem) -> State(x + 1, y, Right, len, rem - 1) - State(x, y, Up, len, rem) -> State(x, y + 1, Up, len, rem - 1) - State(x, y, Left, len, rem) -> State(x - 1, y, Left, len, rem - 1) - State(x, y, Down, len, rem) -> State(x, y - 1, Down, len, rem - 1) - } -} - -type Grid = - dict.Dict(#(Int, Int), Int) - -pub fn parse(input: String) -> Int { - let assert Ok(n) = int.parse(input) - n -} - -pub fn pt_1(input: Int) -> Int { - next_step(1, input, starting_state()) -} - -fn next_step(current: Int, target: Int, state: State) -> Int { - case current == target { - True -> int.absolute_value(state.x) + int.absolute_value(state.y) - False -> next_step(current + 1, target, update_state(state)) - } -} - -pub fn pt_2(input: Int) -> Int { - let grid: Grid = dict.from_list([#(#(0, 0), 1)]) - - add_next_cell(input, starting_state(), grid) -} - -fn neighbors(coord: #(Int, Int)) -> List(#(Int, Int)) { - let #(x, y) = coord - - use dx <- list.flat_map(list.range(-1, 1)) - use dy <- list.map(list.range(-1, 1)) - #(x + dx, y + dy) -} - -fn add_next_cell(target: Int, state: State, grid: Grid) -> Int { - let next_cell = update_state(state) - let coords = #(next_cell.x, next_cell.y) - let value = - list.fold(neighbors(coords), 0, fn(acc, coord) { - case dict.get(grid, coord) { - Ok(n) -> acc + n - _err -> acc - } - }) - - case value >= target { - True -> value - False -> add_next_cell(target, next_cell, dict.insert(grid, coords, value)) - } -} diff --git a/aoc2017-gleam/src/aoc_2017/day_4.gleam b/aoc2017-gleam/src/aoc_2017/day_4.gleam deleted file mode 100644 index 9bc4f9a..0000000 --- a/aoc2017-gleam/src/aoc_2017/day_4.gleam +++ /dev/null @@ -1,31 +0,0 @@ -import gleam/list -import gleam/string - -pub fn parse(input: String) -> List(List(String)) { - use row <- list.map(string.split(input, "\n")) - string.split(row, " ") -} - -pub fn pt_1(input: List(List(String))) { - use acc, passwords <- list.fold(input, 0) - case passwords == list.unique(passwords) { - True -> acc + 1 - False -> acc - } -} - -pub fn pt_2(input: List(List(String))) { - use acc, passwords <- list.fold(input, 0) - let sorted_passwords = list.map(passwords, sort_graphemes) - case sorted_passwords == list.unique(sorted_passwords) { - True -> acc + 1 - False -> acc - } -} - -fn sort_graphemes(word: String) -> String { - word - |> string.to_graphemes - |> list.sort(string.compare) - |> string.concat -} diff --git a/aoc2017-gleam/src/aoc_2017/day_5.gleam b/aoc2017-gleam/src/aoc_2017/day_5.gleam deleted file mode 100644 index a0b9b80..0000000 --- a/aoc2017-gleam/src/aoc_2017/day_5.gleam +++ /dev/null @@ -1,48 +0,0 @@ -import gary.{type ErlangArray} -import gary/array -import gleam/int -import gleam/list -import gleam/result -import gleam/string - -pub fn parse(input: String) -> ErlangArray(Int) { - input - |> string.split("\n") - |> list.map(int.parse) - |> result.values() - |> array.from_list(default: 0) - |> array.make_fixed() -} - -pub fn pt_1(input: ErlangArray(Int)) { - next_step(input, 0, 0, part: 1) -} - -pub fn pt_2(input: ErlangArray(Int)) { - next_step(input, 0, 0, part: 2) -} - -fn next_step( - instructions: ErlangArray(Int), - pointer: Int, - step: Int, - part part: Int, -) { - case array.get(from: instructions, at: pointer) { - Ok(distance) -> { - let delta = delta(distance, part) - let assert Ok(updated_instructions) = - array.set(instructions, at: pointer, put: distance + delta) - next_step(updated_instructions, pointer + distance, step + 1, part) - } - Error(_) -> step - } -} - -fn delta(d: Int, part: Int) { - case part, d { - 1, _ -> 1 - _, n if n < 3 -> 1 - _, _ -> -1 - } -} diff --git a/aoc2017-gleam/src/aoc_2017/day_6.gleam b/aoc2017-gleam/src/aoc_2017/day_6.gleam deleted file mode 100644 index 84222e1..0000000 --- a/aoc2017-gleam/src/aoc_2017/day_6.gleam +++ /dev/null @@ -1,65 +0,0 @@ -import gary.{type ErlangArray} -import gary/array -import gleam/bool -import gleam/int -import gleam/list -import gleam/result -import gleam/set.{type Set} -import gleam/string - -pub fn parse(input) -> ErlangArray(Int) { - input - |> string.split("\t") - |> list.map(int.parse) - |> result.values() - |> array.from_list(default: -1) - |> array.make_fixed() -} - -pub fn pt_1(input: ErlangArray(Int)) { - check_cycle(input, set.from_list([input]), 1).1 -} - -pub fn pt_2(input: ErlangArray(Int)) { - let #(target, cycle) = check_cycle(input, set.from_list([input]), 1) - cycle - check_cycle(input, set.from_list([input, target]), 1).1 -} - -fn get_max(array: ErlangArray(Int)) -> #(Int, Int) { - use index, value, max <- array.fold(over: array, from: #(-1, -1)) - case value > max.1 { - True -> #(index, value) - False -> max - } -} - -fn redistribute( - array: ErlangArray(Int), - pointer: Int, - remaining: Int, -) -> ErlangArray(Int) { - use <- bool.guard(remaining == 0, array) - case array.get(from: array, at: pointer) { - Error(_) -> redistribute(array, 0, remaining) - Ok(n) -> { - let assert Ok(updated_array) = - array.set(into: array, at: pointer, put: n + 1) - redistribute(updated_array, pointer + 1, remaining - 1) - } - } -} - -fn check_cycle( - current: ErlangArray(Int), - cache: Set(ErlangArray(Int)), - cycle: Int, -) -> #(ErlangArray(Int), Int) { - let #(index, to_redistribute) = current |> get_max - let assert Ok(zeroed) = array.set(into: current, at: index, put: 0) - let next = redistribute(zeroed, index + 1, to_redistribute) - - case set.contains(cache, next) { - True -> #(next, cycle) - False -> check_cycle(next, set.insert(cache, next), cycle + 1) - } -} diff --git a/aoc2017-gleam/src/aoc_2017/day_7.gleam b/aoc2017-gleam/src/aoc_2017/day_7.gleam deleted file mode 100644 index a289fa5..0000000 --- a/aoc2017-gleam/src/aoc_2017/day_7.gleam +++ /dev/null @@ -1,108 +0,0 @@ -import gleam/dict.{type Dict} -import gleam/int -import gleam/list -import gleam/option.{Some} -import gleam/regex.{type Match, Match} -import gleam/set -import gleam/string - -pub type Program { - Program(name: String, weight: Int, supporting: List(String)) -} - -pub fn parse(input: String) { - let assert Ok(re) = regex.from_string("([a-z]+) \\(([0-9]+)\\)(?> -> (.*))?") - - use match <- list.map(string.split(input, "\n")) - case regex.scan(re, match) { - [Match(submatches: [Some(name), Some(weight)], ..)] -> - Program(name, to_int(weight), []) - [Match(submatches: [Some(name), Some(weight), Some(supporting)], ..)] -> - Program(name, to_int(weight), string.split(supporting, ", ")) - _ -> panic as { "couldn't parse" <> match } - } -} - -fn to_int(str: String) -> Int { - let assert Ok(n) = int.parse(str) - n -} - -pub fn pt_1(input: List(Program)) { - let supporters = input |> list.map(fn(p) { p.name }) |> set.from_list() - let supporting = - input |> list.flat_map(fn(p) { p.supporting }) |> set.from_list() - - let assert [base] = set.difference(supporters, supporting) |> set.to_list - base -} - -pub fn pt_2(input: List(Program)) { - let weights = - input |> list.map(fn(p) { #(p.name, p.weight) }) |> dict.from_list - - let supporters = - input - |> list.filter_map(fn(p) { - case list.is_empty(p.supporting) { - True -> Error(Nil) - False -> Ok(#(p.name, p.supporting)) - } - }) - |> dict.from_list - - supporters - |> dict.keys() - |> list.filter_map(fn(name) { - let branch_weights = - name - |> branch_weights(weights, supporters) - |> fn(tup: #(String, List(#(Int, String)))) { tup.1 } - - case - branch_weights - |> list.map(fn(tup: #(Int, String)) { tup.0 }) - |> list.unique - |> list.length - { - 2 -> Ok(#(name, branch_weights)) - _ -> Error(Nil) - } - }) - - // [ - // #("hmgrlpj", [#(2078, "drjmjug"), #(2070, "nigdlq"), #(2070, "omytneg"), ...]), - // #("smaygo", [#(14564, "hmgrlpj"), #(14556, "fbnbt"), #(14556, "nfdvsc")]) - // #("eugwuhl", [#(48292, "smaygo"), #(48284, "pvvbn"), #(48284, "hgizeb"), ...]), - // ] - // - // by inspection, eugwuhl -> smaygo -> hmgrlpj -> drjmjug; changing drjmjug will fix the tower - - let assert Ok(w) = dict.get(weights, "drjmjug") - w - 8 -} - -fn branch_weights( - name: String, - weights: Dict(String, Int), - supporting: Dict(String, List(String)), -) -> #(String, List(#(Int, String))) { - let supported = case dict.get(supporting, name) { - Ok(supported) -> supported - Error(_) -> [] - } - - let supported_weights = - list.map(supported, fn(s) { - let assert Ok(weight) = dict.get(weights, s) - let children_weights = - s - |> branch_weights(weights, supporting) - |> fn(tup: #(String, List(#(Int, String)))) { tup.1 } - |> list.map(fn(tup: #(Int, String)) { tup.0 }) - weight + int.sum(children_weights) - }) - |> list.zip(supported) - - #(name, supported_weights) -} diff --git a/aoc2017-gleam/src/aoc_2017/day_8.gleam b/aoc2017-gleam/src/aoc_2017/day_8.gleam deleted file mode 100644 index 2f9d0dc..0000000 --- a/aoc2017-gleam/src/aoc_2017/day_8.gleam +++ /dev/null @@ -1,131 +0,0 @@ -import gleam/dict -import gleam/int -import gleam/list -import gleam/option.{None, Some} -import gleam/string - -const max_register = "__MAX" - -pub type Instruction { - Instruction(register: String, op: Operation, condition: Condition) -} - -pub type Operation { - Inc(by: Int) - Dec(by: Int) -} - -pub type Condition { - Equal(register: String, value: Int) - NotEqual(register: String, value: Int) - LessThan(register: String, value: Int) - GreaterThan(register: String, value: Int) - LessThanOrEq(register: String, value: Int) - GreaterThanOrEq(register: String, value: Int) -} - -type Registers = - dict.Dict(String, Int) - -pub fn parse(input: String) { - input - |> string.split("\n") - |> list.map(parse_instruction) -} - -fn parse_instruction(str: String) -> Instruction { - case string.split(str, " ") { - [name, op, by, "if", cond_name, cond_type, cond_by] -> - Instruction(name, to_op(op, by), to_cond(cond_name, cond_type, cond_by)) - _ -> panic as { "couldn't parse: " <> str } - } -} - -pub fn pt_1(input: List(Instruction)) { - let registers = dict.new() |> dict.insert(max_register, 0) - - input - |> list.fold(registers, next_instruction) - |> dict.delete(max_register) - |> dict.values() - |> list.reduce(int.max) -} - -fn next_instruction(regs: Registers, inst: Instruction) { - case to_compare_fn(inst.condition)(fetch(inst.condition.register, regs)) { - True -> { - let updated_regs = dict.update(regs, inst.register, to_update_fn(inst.op)) - let assert Ok(max) = updated_regs |> dict.values |> list.reduce(int.max) - dict.insert(updated_regs, max_register, max) - } - False -> regs - } -} - -pub fn pt_2(input: List(Instruction)) { - let registers = dict.new() |> dict.insert(max_register, 0) - - input - |> list.fold(registers, next_instruction) - |> dict.get(max_register) -} - -fn int(str: String) -> Int { - let assert Ok(n) = int.parse(str) - n -} - -fn to_op(raw_op: String, raw_by: String) -> Operation { - case raw_op { - "inc" -> Inc(int(raw_by)) - "dec" -> Dec(int(raw_by)) - _ -> panic as { "bad op: " <> raw_op } - } -} - -fn to_cond(name: String, raw_type: String, raw_by: String) -> Condition { - case raw_type { - "==" -> Equal(name, int(raw_by)) - "!=" -> NotEqual(name, int(raw_by)) - ">" -> GreaterThan(name, int(raw_by)) - "<" -> LessThan(name, int(raw_by)) - ">=" -> GreaterThanOrEq(name, int(raw_by)) - "<=" -> LessThanOrEq(name, int(raw_by)) - _ -> panic as { "bad condition: " <> raw_type } - } -} - -fn to_compare_fn(condition: Condition) -> fn(Int) -> Bool { - case condition { - Equal(value: v, ..) -> fn(a) { a == v } - NotEqual(value: v, ..) -> fn(a) { a != v } - GreaterThan(value: v, ..) -> fn(a) { a > v } - LessThan(value: v, ..) -> fn(a) { a < v } - GreaterThanOrEq(value: v, ..) -> fn(a) { a >= v } - LessThanOrEq(value: v, ..) -> fn(a) { a <= v } - } -} - -fn to_update_fn(op: Operation) { - case op { - Inc(n) -> fn(x) { - case x { - Some(i) -> i + n - None -> n - } - } - Dec(n) -> fn(x) { - case x { - Some(i) -> i - n - None -> -n - } - } - } -} - -fn fetch(name: String, registers: Registers) -> Int { - case dict.get(registers, name) { - Ok(n) -> n - Error(_) -> 0 - } -} diff --git a/aoc2017-gleam/src/aoc_2017/day_9.gleam b/aoc2017-gleam/src/aoc_2017/day_9.gleam deleted file mode 100644 index 90eb4b3..0000000 --- a/aoc2017-gleam/src/aoc_2017/day_9.gleam +++ /dev/null @@ -1,48 +0,0 @@ -import gleam/list -import gleam/option.{Some} -import gleam/regex -import gleam/string - -pub fn parse(input: String) { - let assert Ok(cancel) = regex.from_string("!.") - - replace(input, with: cancel) -} - -pub fn pt_1(input: String) { - input - |> strip_to_brackets() - |> next_bracket(0, 1) -} - -fn replace(input: String, with regex: regex.Regex) -> String { - input |> regex.split(with: regex) |> string.concat() -} - -fn strip_to_brackets(input: String) -> String { - let assert Ok(garbage) = regex.from_string("<.*?>") - let assert Ok(not_group) = regex.from_string("[^{}]") - - input - |> replace(with: garbage) - |> replace(with: not_group) -} - -fn next_bracket(brackets: String, score: Int, depth: Int) -> Int { - case string.pop_grapheme(brackets) { - Error(Nil) -> score - Ok(#("{", rest)) -> next_bracket(rest, score + depth, depth + 1) - Ok(#("}", rest)) -> next_bracket(rest, score, depth - 1) - _ -> panic as "unrecognized character" - } -} - -pub fn pt_2(input: String) { - let assert Ok(garbage) = regex.from_string("<(.*?)>") - - use acc, match <- list.fold(regex.scan(input, with: garbage), 0) - case match.submatches { - [Some(g)] -> string.length(g) + acc - _ -> acc - } -} diff --git a/aoc2017-gleam/src/helpers/set_state.gleam b/aoc2017-gleam/src/helpers/set_state.gleam deleted file mode 100644 index cbbad81..0000000 --- a/aoc2017-gleam/src/helpers/set_state.gleam +++ /dev/null @@ -1,55 +0,0 @@ -import gleam/erlang/process.{type Subject, Normal} -import gleam/option.{None} -import gleam/otp/actor.{type Next, Continue, Stop} -import gleam/set.{type Set} - -const timeout = 1000 - -pub type Message(k) { - Shutdown - Check(key: k, client: Subject(Bool)) - Add(key: k) - Drop(key: k) - Pop(client: Subject(Result(k, Nil))) -} - -fn handle_message(message: Message(k), set: Set(k)) -> Next(Message(k), Set(k)) { - case message { - Shutdown -> Stop(Normal) - Check(key, client) -> { - process.send(client, set.contains(set, key)) - Continue(set, None) - } - Add(key) -> Continue(set.insert(set, key), None) - Drop(key) -> Continue(set.delete(set, key), None) - Pop(client) -> { - case set.to_list(set) { - [next, ..] -> { - process.send(client, Ok(next)) - Continue(set.delete(set, next), None) - } - [] -> { - process.send(client, Error(Nil)) - Stop(Normal) - } - } - } - } -} - -pub fn start_actor(with: Set(a)) { - let assert Ok(actor) = actor.start(with, handle_message) - actor -} - -pub fn pop(actor) { - process.call(actor, Pop, timeout) -} - -pub fn check(actor, value) { - process.call(actor, Check(value, _), timeout) -} - -pub fn drop(actor, value) { - process.send(actor, Drop(value)) -} diff --git a/aoc2017-gleam/test/aoc2017_gleam_test.gleam b/aoc2017-gleam/test/aoc2017_gleam_test.gleam deleted file mode 100644 index 3831e7a..0000000 --- a/aoc2017-gleam/test/aoc2017_gleam_test.gleam +++ /dev/null @@ -1,12 +0,0 @@ -import gleeunit -import gleeunit/should - -pub fn main() { - gleeunit.main() -} - -// gleeunit test functions end in `_test` -pub fn hello_world_test() { - 1 - |> should.equal(1) -} diff --git a/aoc2018/day-01/day-01.rkt b/aoc2018/day-01/day-01.rkt deleted file mode 100644 index b18f7c9..0000000 --- a/aoc2018/day-01/day-01.rkt +++ /dev/null @@ -1,16 +0,0 @@ -#lang racket - -(require advent-of-code - threading) - -(define deltas - (~>> (open-aoc-input (find-session) 2018 1 #:cache #true) port->lines (map string->number))) - -;; part 1 -(for/sum ([delta deltas]) delta) - -;; part 2 -(for/fold ([seen (set)] [current-frequency 0] #:result current-frequency) ([delta (in-cycle deltas)]) - (define new-frequency (+ current-frequency delta)) - #:final (set-member? seen new-frequency) - (values (set-add seen new-frequency) new-frequency)) diff --git a/aoc2018/day-02/day-02.rkt b/aoc2018/day-02/day-02.rkt deleted file mode 100644 index 38155fb..0000000 --- a/aoc2018/day-02/day-02.rkt +++ /dev/null @@ -1,27 +0,0 @@ -#lang racket - -(require advent-of-code - threading) - -(define ids (port->lines (open-aoc-input (find-session) 2018 2 #:cache #true))) - -;; part 1 -(define (make-baskets str) - (for/fold ([baskets (hash)]) ([ch (in-string str)]) - (hash-update baskets ch add1 0))) - -(define (has-count n ht) - (member n (hash-values ht))) - -(for/fold ([two 0] [three 0] #:result (* two three)) ([id (in-list ids)]) - (define baskets (make-baskets id)) - (values (if (has-count 2 baskets) (add1 two) two) (if (has-count 3 baskets) (add1 three) three))) - -;; part 2 -(define (string-difference str1 str2) - (for/sum ([ch1 (in-string str1)] [ch2 (in-string str2)]) (if (equal? ch1 ch2) 0 1))) - -(for*/first ([id1 (in-list ids)] [id2 (in-list ids)] #:when (= 1 (string-difference id1 id2))) - (~>> (for/list ([ch1 (in-string id1)] [ch2 (in-string id2)] #:when (equal? ch1 ch2)) - ch1) - (apply string))) diff --git a/aoc2018/day-03/day-03.rkt b/aoc2018/day-03/day-03.rkt deleted file mode 100644 index b486361..0000000 --- a/aoc2018/day-03/day-03.rkt +++ /dev/null @@ -1,51 +0,0 @@ -#lang racket - -(require advent-of-code - threading - data/applicative - data/monad - megaparsack - megaparsack/text) - -(struct claim (number start-x start-y size-x size-y) #:transparent) - -(define claim/p - (do (char/p #\#) - [number <- integer/p] - (string/p " @ ") - [start-x <- integer/p] - (char/p #\,) - [start-y <- integer/p] - (string/p ": ") - [size-x <- integer/p] - (char/p #\x) - [size-y <- integer/p] - (pure (claim number start-x start-y size-x size-y)))) - -(define (parse-claim str) - (parse-result! (parse-string claim/p str))) - -(define (make-claim ht cl) - (for*/fold ([fabric ht]) - ([x (in-range (claim-start-x cl) (+ (claim-start-x cl) (claim-size-x cl)))] - [y (in-range (claim-start-y cl) (+ (claim-start-y cl) (claim-size-y cl)))]) - (hash-update fabric (cons x y) add1 0))) - -(define claims - (~> (port->lines (open-aoc-input (find-session) 2018 3 #:cache #true)) (map parse-claim _))) - -(define claimed-fabric - (for/fold ([fabric (hash)]) ([cl (in-list claims)]) - (make-claim fabric cl))) - -;; part 1 -(for/sum ([claim-count (in-list (hash-values claimed-fabric))] #:when (< 1 claim-count)) 1) - -;; part 2 -(define (uncontested-claim? fabric cl) - (for*/and ([x (in-range (claim-start-x cl) (+ (claim-start-x cl) (claim-size-x cl)))] - [y (in-range (claim-start-y cl) (+ (claim-start-y cl) (claim-size-y cl)))]) - (= 1 (hash-ref fabric (cons x y))))) - -(for/first ([cl (in-list claims)] #:when (uncontested-claim? claimed-fabric cl)) - (claim-number cl)) diff --git a/aoc2018/day-04/day-04.rkt b/aoc2018/day-04/day-04.rkt deleted file mode 100644 index 3660099..0000000 --- a/aoc2018/day-04/day-04.rkt +++ /dev/null @@ -1,43 +0,0 @@ -#lang racket - -(require advent-of-code - data/applicative - data/monad - megaparsack - megaparsack/text - threading) - -(struct entry (month day hour minute message) #:transparent) - -(define (parse-message chrs) - (define str (apply string chrs)) - (match str - ["wakes up" 'awake] - ["falls asleep" 'asleep] - [shift (~> shift (string-trim "Guard #") (string-trim " begins shift") string->number)])) - -(define entry/p - (do (string/p "[1518-") - [month <- integer/p] - (char/p #\-) - [day <- integer/p] - space/p - [hour <- integer/p] - (char/p #\:) - [minute <- integer/p] - (string/p "] ") - [message <- (many/p any-char/p)] - (pure (entry month day hour minute (parse-message message))))) - -(define (parse-entry str) - (parse-result! (parse-string entry/p str))) - -(define entries - (~> (port->lines (open-aoc-input (find-session) 2018 4 #:cache #true)) (map parse-entry _))) - -(define sorted-entries - (~> entries - (sort < #:key entry-minute) - (sort < #:key entry-hour) - (sort < #:key entry-day) - (sort < #:key entry-month))) diff --git a/aoc2018/day-05/day-05.rkt b/aoc2018/day-05/day-05.rkt deleted file mode 100644 index a78f5b5..0000000 --- a/aoc2018/day-05/day-05.rkt +++ /dev/null @@ -1,31 +0,0 @@ -#lang racket - -(require advent-of-code - threading) - -(define starting-chain - (~> (fetch-aoc-input (find-session) 2018 5 #:cache #true) string-trim string->list)) - -(define (reactive-pair? ch1 ch2) - (and (equal? (char-downcase ch1) (char-downcase ch2)) (not (equal? ch1 ch2)))) - -(define (remove-reactive-pairs chs [acc '()]) - (match chs - [(list* ch1 ch2 rest-chs) - #:when (reactive-pair? ch1 ch2) - (remove-reactive-pairs rest-chs acc)] - [(list* ch rest-chs) (remove-reactive-pairs rest-chs (cons ch acc))] - [(list) (reverse acc)])) - -(define (keep-removing-reactive-pairs chs) - (define chs* (remove-reactive-pairs chs)) - (if (equal? chs chs*) (length chs) (keep-removing-reactive-pairs chs*))) - -;; part 1 -(keep-removing-reactive-pairs starting-chain) - -;; part 2 -(~>> (for/list ([letter (in-string "abcdefghijklmnopqrstuvwxyz")]) - (define tweaked-chain (filter (λ (c) (not (equal? (char-downcase c) letter))) starting-chain)) - (keep-removing-reactive-pairs tweaked-chain)) - (apply min)) diff --git a/aoc2018/day-06/day-06.rkt b/aoc2018/day-06/day-06.rkt deleted file mode 100644 index 6f1f7b4..0000000 --- a/aoc2018/day-06/day-06.rkt +++ /dev/null @@ -1 +0,0 @@ -#lang racket diff --git a/aoc2019-gleam/.github/workflows/test.yml b/aoc2019-gleam/.github/workflows/test.yml deleted file mode 100644 index dd5e246..0000000 --- a/aoc2019-gleam/.github/workflows/test.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: test - -on: - push: - branches: - - master - - main - pull_request: - -jobs: - test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: erlef/setup-beam@v1 - with: - otp-version: "26.0.2" - gleam-version: "1.2.0" - rebar3-version: "3" - # elixir-version: "1.15.4" - - run: gleam deps download - - run: gleam test - - run: gleam format --check src test diff --git a/aoc2019-gleam/.gitignore b/aoc2019-gleam/.gitignore deleted file mode 100644 index 599be4e..0000000 --- a/aoc2019-gleam/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -*.beam -*.ez -/build -erl_crash.dump diff --git a/aoc2019-gleam/README.md b/aoc2019-gleam/README.md deleted file mode 100644 index bbf8121..0000000 --- a/aoc2019-gleam/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# aoc2019_gleam - -[![Package Version](https://img.shields.io/hexpm/v/aoc2019_gleam)](https://hex.pm/packages/aoc2019_gleam) -[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/aoc2019_gleam/) - -```sh -gleam add aoc2019_gleam -``` -```gleam -import aoc2019_gleam - -pub fn main() { - // TODO: An example of the project in use -} -``` - -Further documentation can be found at . - -## Development - -```sh -gleam run # Run the project -gleam test # Run the tests -gleam shell # Run an Erlang shell -``` diff --git a/aoc2019-gleam/gleam.toml b/aoc2019-gleam/gleam.toml deleted file mode 100644 index c9ede3d..0000000 --- a/aoc2019-gleam/gleam.toml +++ /dev/null @@ -1,21 +0,0 @@ -name = "aoc2019_gleam" -version = "1.0.0" - -# Fill out these fields if you intend to generate HTML documentation or publish -# your project to the Hex package manager. -# -# description = "" -# licences = ["Apache-2.0"] -# repository = { type = "github", user = "username", repo = "project" } -# links = [{ title = "Website", href = "https://gleam.run" }] -# -# For a full reference of all the available options, you can have a look at -# https://gleam.run/writing-gleam/gleam-toml/. - -[dependencies] -gleam_stdlib = ">= 0.34.0 and < 2.0.0" -gladvent = ">= 0.7.3 and < 1.0.0" -gary = ">= 1.0.1 and < 2.0.0" - -[dev-dependencies] -gleeunit = ">= 1.0.0 and < 2.0.0" diff --git a/aoc2019-gleam/manifest.toml b/aoc2019-gleam/manifest.toml deleted file mode 100644 index 12fa60f..0000000 --- a/aoc2019-gleam/manifest.toml +++ /dev/null @@ -1,33 +0,0 @@ -# This file was generated by Gleam -# You typically do not need to edit this file - -packages = [ - { name = "argv", version = "1.0.2", build_tools = ["gleam"], requirements = [], otp_app = "argv", source = "hex", outer_checksum = "BA1FF0929525DEBA1CE67256E5ADF77A7CDDFE729E3E3F57A5BDCAA031DED09D" }, - { name = "filepath", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "EFB6FF65C98B2A16378ABC3EE2B14124168C0CE5201553DE652E2644DCFDB594" }, - { name = "gary", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gary", source = "hex", outer_checksum = "4C05611EEC74876A1E36309EED22C826B92F22E566579ACBC65C579CD615EC60" }, - { name = "gladvent", version = "0.7.3", build_tools = ["gleam"], requirements = ["argv", "filepath", "gleam_erlang", "gleam_json", "gleam_otp", "gleam_package_interface", "gleam_stdlib", "glint", "parallel_map", "shellout", "simplifile", "snag", "spinner", "tom"], otp_app = "gladvent", source = "hex", outer_checksum = "59D93FD759427BCE8EE9828C0B62A3CD18362225F948C9789D334DCEAD621358" }, - { name = "gleam_community_ansi", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_community_colour", "gleam_stdlib"], otp_app = "gleam_community_ansi", source = "hex", outer_checksum = "FE79E08BF97009729259B6357EC058315B6FBB916FAD1C2FF9355115FEB0D3A4" }, - { name = "gleam_community_colour", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib"], otp_app = "gleam_community_colour", source = "hex", outer_checksum = "795964217EBEDB3DA656F5EB8F67D7AD22872EB95182042D3E7AFEF32D3FD2FE" }, - { name = "gleam_erlang", version = "0.25.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "054D571A7092D2A9727B3E5D183B7507DAB0DA41556EC9133606F09C15497373" }, - { name = "gleam_json", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "thoas"], otp_app = "gleam_json", source = "hex", outer_checksum = "9063D14D25406326C0255BDA0021541E797D8A7A12573D849462CAFED459F6EB" }, - { name = "gleam_otp", version = "0.10.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "0B04FE915ACECE539B317F9652CAADBBC0F000184D586AAAF2D94C100945D72B" }, - { name = "gleam_package_interface", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib"], otp_app = "gleam_package_interface", source = "hex", outer_checksum = "52A721BCA972C8099BB881195D821AAA64B9F2655BECC102165D5A1097731F01" }, - { name = "gleam_stdlib", version = "0.38.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "663CF11861179AF415A625307447775C09404E752FF99A24E2057C835319F1BE" }, - { name = "glearray", version = "0.2.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "glearray", source = "hex", outer_checksum = "9C207E05F38D724F464FA921378DB3ABC2B0A2F5821116D8BC8B2CACC68930D5" }, - { name = "gleeunit", version = "1.1.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "72CDC3D3F719478F26C4E2C5FED3E657AC81EC14A47D2D2DEBB8693CA3220C3B" }, - { name = "glint", version = "1.0.0-rc2", build_tools = ["gleam"], requirements = ["gleam_community_ansi", "gleam_community_colour", "gleam_stdlib", "snag"], otp_app = "glint", source = "hex", outer_checksum = "FD5C47CE237CA67121F3946ADE7C630750BB67F5E8A4717D2DF5B5EE758CCFDB" }, - { name = "parallel_map", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_otp", "gleam_stdlib"], otp_app = "parallel_map", source = "hex", outer_checksum = "653714A9FD63EACD1A9D0A6582A972B0EC109AE275CDDD2E99CFC3DFAFAB9225" }, - { name = "repeatedly", version = "2.1.1", build_tools = ["gleam"], requirements = [], otp_app = "repeatedly", source = "hex", outer_checksum = "38808C3EC382B0CD981336D5879C24ECB37FCB9C1D1BD128F7A80B0F74404D79" }, - { name = "shellout", version = "1.6.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "shellout", source = "hex", outer_checksum = "E2FCD18957F0E9F67E1F497FC9FF57393392F8A9BAEAEA4779541DE7A68DD7E0" }, - { name = "simplifile", version = "1.7.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "1D5DFA3A2F9319EC85825F6ED88B8E449F381B0D55A62F5E61424E748E7DDEB0" }, - { name = "snag", version = "0.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "snag", source = "hex", outer_checksum = "54D32E16E33655346AA3E66CBA7E191DE0A8793D2C05284E3EFB90AD2CE92BCC" }, - { name = "spinner", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_community_ansi", "gleam_erlang", "gleam_stdlib", "glearray", "repeatedly"], otp_app = "spinner", source = "hex", outer_checksum = "200BA3D4A04D468898E63C0D316E23F526E02514BC46454091975CB5BAE41E8F" }, - { name = "thoas", version = "1.2.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "E38697EDFFD6E91BD12CEA41B155115282630075C2A727E7A6B2947F5408B86A" }, - { name = "tom", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "tom", source = "hex", outer_checksum = "A5364613E3DBF77F38EFF81DA9F99324086D029EC2B2D44348762FBE38602311" }, -] - -[requirements] -gary = { version = ">= 1.0.1 and < 2.0.0"} -gladvent = { version = ">= 0.7.3 and < 1.0.0" } -gleam_stdlib = { version = ">= 0.34.0 and < 2.0.0" } -gleeunit = { version = ">= 1.0.0 and < 2.0.0" } diff --git a/aoc2019-gleam/src/aoc2019_gleam.gleam b/aoc2019-gleam/src/aoc2019_gleam.gleam deleted file mode 100644 index c5a7e0a..0000000 --- a/aoc2019-gleam/src/aoc2019_gleam.gleam +++ /dev/null @@ -1,5 +0,0 @@ -import gladvent - -pub fn main() { - gladvent.main() -} diff --git a/aoc2019-gleam/src/aoc_2019/day_1.gleam b/aoc2019-gleam/src/aoc_2019/day_1.gleam deleted file mode 100644 index 8a7fd2d..0000000 --- a/aoc2019-gleam/src/aoc_2019/day_1.gleam +++ /dev/null @@ -1,30 +0,0 @@ -import gleam/int -import gleam/list -import gleam/result -import gleam/string - -pub fn parse(input: String) -> List(Int) { - input - |> string.split("\n") - |> list.map(int.parse) - |> result.values() -} - -pub fn pt_1(input: List(Int)) { - list.fold(input, 0, fn(total, next) { total + naive_fuel(next) }) -} - -pub fn pt_2(input: List(Int)) { - list.fold(input, 0, fn(total, next) { total + recursive_fuel(next) }) -} - -fn naive_fuel(weight: Int) -> Int { - { weight / 3 } - 2 -} - -fn recursive_fuel(weight: Int) -> Int { - case { weight / 3 } - 2 { - n if n <= 0 -> 0 - n -> n + recursive_fuel(n) - } -} diff --git a/aoc2019-gleam/src/aoc_2019/day_2.gleam b/aoc2019-gleam/src/aoc_2019/day_2.gleam deleted file mode 100644 index 8faa0ea..0000000 --- a/aoc2019-gleam/src/aoc_2019/day_2.gleam +++ /dev/null @@ -1,81 +0,0 @@ -import gary.{type ErlangArray} -import gary/array.{type ArrayError} -import gleam/bool -import gleam/int -import gleam/list -import gleam/result -import gleam/string - -pub fn parse(input: String) -> ErlangArray(Int) { - input - |> string.split(",") - |> list.map(int.parse) - |> result.values() - |> array.from_list(default: -1) - |> array.make_fixed() -} - -pub fn pt_1(input: ErlangArray(Int)) -> Int { - let assert Ok(result) = - input - |> edit_starting_intcodes(12, 2) - |> run_intcode(0) - - result -} - -pub fn pt_2(input: ErlangArray(Int)) -> Int { - let assert [result] = { - use noun <- list.flat_map(list.range(0, 99)) - use verb <- list.filter_map(list.range(0, 99)) - let result = input |> edit_starting_intcodes(noun, verb) |> run_intcode(0) - case result == Ok(19_690_720) { - True -> Ok(100 * noun + verb) - False -> Error(Nil) - } - } - - result -} - -fn run_intcode( - intcode: ErlangArray(Int), - pointer: Int, -) -> Result(Int, ArrayError) { - let assert Ok(op_code) = array.get(intcode, pointer) - let op = get_op(op_code) - - use <- bool.guard(result.is_error(op), array.get(intcode, 0)) - let assert Ok(position_1) = array.get(intcode, pointer + 1) - let assert Ok(position_2) = array.get(intcode, pointer + 2) - let assert Ok(position_3) = array.get(intcode, pointer + 3) - - let assert Ok(value_1) = array.get(intcode, position_1) - let assert Ok(value_2) = array.get(intcode, position_2) - - let assert Ok(f) = op - let new_value = f(value_1, value_2) - let assert Ok(updated_intcode) = array.set(intcode, position_3, new_value) - run_intcode(updated_intcode, pointer + 4) -} - -fn edit_starting_intcodes( - intcodes: ErlangArray(Int), - new_code_1: Int, - new_code_2: Int, -) -> ErlangArray(Int) { - let assert Ok(updated) = - intcodes - |> array.set(at: 1, put: new_code_1) - |> result.try(array.set(into: _, at: 2, put: new_code_2)) - updated -} - -fn get_op(code: Int) -> Result(fn(Int, Int) -> Int, Nil) { - case code { - 1 -> Ok(int.add) - 2 -> Ok(int.multiply) - 99 -> Error(Nil) - _ -> panic as "bad opcode" - } -} diff --git a/aoc2019-gleam/test/aoc2019_gleam_test.gleam b/aoc2019-gleam/test/aoc2019_gleam_test.gleam deleted file mode 100644 index 3831e7a..0000000 --- a/aoc2019-gleam/test/aoc2019_gleam_test.gleam +++ /dev/null @@ -1,12 +0,0 @@ -import gleeunit -import gleeunit/should - -pub fn main() { - gleeunit.main() -} - -// gleeunit test functions end in `_test` -pub fn hello_world_test() { - 1 - |> should.equal(1) -} diff --git a/aoc2019/day-02/day-02.rkt b/aoc2019/day-02/day-02.rkt deleted file mode 100644 index 56019e8..0000000 --- a/aoc2019/day-02/day-02.rkt +++ /dev/null @@ -1,32 +0,0 @@ -#lang racket - -(require advent-of-code - threading) - -(define initial-opcodes - (parameterize ([current-readtable (make-readtable #f #\, #\space #f)]) - (port->list read (open-aoc-input (find-session) 2019 2 #:cache #true)))) - -;; part 1 -(define (edit-opcode ocs oc-1 oc-2) - (~> ocs (list-set 1 oc-1) (list-set 2 oc-2))) - -(define (run-intcode ocs) - (for/fold ([ocs ocs] #:result (car ocs)) - ([pointer (in-range 0 (length initial-opcodes) 4)] #:break (= 99 (list-ref ocs pointer))) - (define op - (match (list-ref ocs pointer) - [1 +] - [2 *])) - (list-set ocs - (list-ref ocs (+ 3 pointer)) - (op (list-ref ocs (list-ref ocs (+ 1 pointer))) - (list-ref ocs (list-ref ocs (+ 2 pointer))))))) - -(~> initial-opcodes (edit-opcode 12 2) run-intcode) - -;; part 2 -(for*/first ([noun (inclusive-range 0 99)] - [verb (inclusive-range 0 99)] - #:when (~> initial-opcodes (edit-opcode noun verb) run-intcode (= 19690720))) - (+ (* 100 noun) verb)) diff --git a/aoc2019/day-03/day-03.rkt b/aoc2019/day-03/day-03.rkt deleted file mode 100644 index 6da3a07..0000000 --- a/aoc2019/day-03/day-03.rkt +++ /dev/null @@ -1,52 +0,0 @@ -#lang racket - -(require advent-of-code - threading - fancy-app - racket/hash) - -(define/match (instruction-parse _str) - [((regexp #px"(.)(.+)" (list _ (app string->symbol dir) (app string->number amt)))) (cons dir amt)]) - -(define (wire-parse str) - (~> str (string-split ",") (map instruction-parse _))) - -(define wires - (~>> (fetch-aoc-input (find-session) 2019 3 #:cache #true) string-split (map wire-parse))) - -(define (manhattan-distance-from-origin p) - (+ (abs (car p)) (abs (cdr p)))) - -(define (trace-wire-path wire) - (for/fold ([path (hash)] [x 0] [y 0] [len 0] #:result path) ([inst (in-list wire)]) - (define-values (x* y*) - (match inst - [(cons 'U dy) (values x (+ y dy))] - [(cons 'D dy) (values x (- y dy))] - [(cons 'R dx) (values (+ x dx) y)] - [(cons 'L dx) (values (- x dx) y)])) - (define next-segment - (for*/list ([new-x (inclusive-range x x* (if (< x x*) 1 -1))] - [new-y (inclusive-range y y* (if (< y y*) 1 -1))]) - (cons new-x new-y))) - (define numbered-segments - (for/hash ([segment (in-list next-segment)] [new-len (in-naturals len)]) - (values segment new-len))) - (values (hash-union path numbered-segments #:combine (λ (v0 _) v0)) x* y* (+ len (cdr inst))))) - -;; part 1 -(define wire-paths (map trace-wire-path wires)) - -(define intersections - (~>> wire-paths - (map (λ~> hash-keys list->set)) - (apply set-intersect) - (set-remove _ '(0 . 0)) - set->list)) - -(~>> intersections (map manhattan-distance-from-origin) (apply min)) - -;; part 2 -(~>> (for/list ([intersection (in-list intersections)]) - (~>> wire-paths (map (hash-ref _ intersection)) (apply +))) - (apply min)) diff --git a/aoc2019/day-04/day-04.rkt b/aoc2019/day-04/day-04.rkt deleted file mode 100644 index 9518779..0000000 --- a/aoc2019/day-04/day-04.rkt +++ /dev/null @@ -1,39 +0,0 @@ -#lang racket - -(define (number->digits n [acc '()]) - (cond - [(< n 10) (cons n acc)] - [else (number->digits (quotient n 10) (cons (remainder n 10) acc))])) - -(define (always-increasing? xs) - (match xs - [(list _) #t] - [(list* a b _) #:when (<= a b) (always-increasing? (cdr xs))] - [_ #f])) - -(define (adjacent-pair? xs) - (match xs - [(list _) #f] - [(list* a a _) a] - [_ (adjacent-pair? (cdr xs))])) - -;; part 1 -(for/sum ([password (inclusive-range 125730 579381)] - #:do [(define digits (number->digits password))] - #:when ((conjoin always-increasing? adjacent-pair?) digits)) - 1) - -;; part 2 -(define (not-in-adjacent-triplet? xs) - (match xs - [(list a a c _ _ _) #:when (not (= a c)) #t] - [(list b a a c _ _) #:when (not (or (= a b) (= a c))) #t] - [(list _ b a a c _) #:when (not (or (= a b) (= a c))) #t] - [(list _ _ b a a c) #:when (not (or (= a b) (= a c))) #t] - [(list _ _ _ b a a) #:when (not (= a b)) #t] - [_ #f])) - -(for/sum ([password (inclusive-range 125730 579381)] - #:do [(define digits (number->digits password))] - #:when ((conjoin always-increasing? adjacent-pair? not-in-adjacent-triplet?) digits)) - 1) \ No newline at end of file diff --git a/aoc2019/day-05/day-05.rkt b/aoc2019/day-05/day-05.rkt deleted file mode 100644 index 6f1f7b4..0000000 --- a/aoc2019/day-05/day-05.rkt +++ /dev/null @@ -1 +0,0 @@ -#lang racket diff --git a/aoc2020/day-01/day-01.rkt b/aoc2020/day-01/day-01.rkt deleted file mode 100644 index e31c45c..0000000 --- a/aoc2020/day-01/day-01.rkt +++ /dev/null @@ -1,20 +0,0 @@ -#lang racket -(require "../../jj-aoc.rkt" - threading) - -(define entries (~>> (open-day 01 2020) (port->list read) list->set)) - -;; part 1 -(define (look-for-complement xs) - (define x (set-first xs)) - (cond - [(set-member? xs (- 2020 x)) (* x (- 2020 x))] - [else (look-for-complement (set-rest xs))])) - -(time (look-for-complement entries)) - -;; part 2 -(time (for*/first ([x (in-set entries)] - [y (in-set (set-subtract entries (set x)))] - #:when (set-member? (set-subtract entries (set x y)) (- 2020 x y))) - (* x y (- 2020 x y)))) diff --git a/aoc2020/day-02/day-02.rkt b/aoc2020/day-02/day-02.rkt deleted file mode 100644 index 9e22a1a..0000000 --- a/aoc2020/day-02/day-02.rkt +++ /dev/null @@ -1,33 +0,0 @@ -#lang racket -(require "../../jj-aoc.rkt" - threading) - -(struct policy (least most char pwd) #:transparent) - -(define (make-policy least-str most-str char-str pwd) - (policy (string->number least-str) (string->number most-str) (string-ref char-str 0) pwd)) - -(define policies - (for/list ([l (in-lines (open-day 02 2020))]) - (~>> l (regexp-match #px"(\\d+)-(\\d+) (\\w): (.*)") rest (apply make-policy)))) - -;; part 1 -(define (valid-policy? p) - (~>> p - policy-pwd - string->list - (count (curry char=? (policy-char p))) - ((λ (n) (and (>= n (policy-least p)) (<= n (policy-most p))))))) - -(for/sum ([p (in-list policies)] #:when (valid-policy? p)) 1) - -;; part 2 -(define (valid-revised-policy? p) - (~>> p - policy-pwd - string->list - ((λ (lst) (list (list-ref lst (sub1 (policy-most p))) (list-ref lst (sub1 (policy-least p)))))) - (count (curry char=? (policy-char p))) - (= 1))) - -(for/sum ([p (in-list policies)] #:when (valid-revised-policy? p)) 1) diff --git a/aoc2020/day-03/day-03.rkt b/aoc2020/day-03/day-03.rkt deleted file mode 100644 index ee9edcf..0000000 --- a/aoc2020/day-03/day-03.rkt +++ /dev/null @@ -1,16 +0,0 @@ -#lang racket -(require "../../jj-aoc.rkt" - threading) - -(define (check-for-trees run rise) - (for*/sum ([(row i) (in-indexed (port->lines (open-day 3 2020)))] - #:when (= 0 (modulo i rise)) - [possible-tree (in-value (sequence-ref (in-cycle row) (* (/ run rise) i)))] - #:when (and (char=? possible-tree #\#))) - 1)) - -;; part 1 -(check-for-trees 3 1) - -;; part 2 -(~>> '((1 1) (3 1) (5 1) (7 1) (1 2)) (map (curry apply check-for-trees)) (apply *)) diff --git a/aoc2020/day-04/day-04.rkt b/aoc2020/day-04/day-04.rkt deleted file mode 100644 index 54d50f8..0000000 --- a/aoc2020/day-04/day-04.rkt +++ /dev/null @@ -1,64 +0,0 @@ -#lang racket -(require "../../jj-aoc.rkt" - threading) - -(define passports - (~> (open-day 4 2020) (port->string) (string-split "\n\n") (map (λ~> (string-replace "\n" " ")) _))) - -;; part 1 -(define required-fields (list "byr:" "iyr:" "eyr:" "hgt:" "hcl:" "ecl:" "pid:")) - -(define (valid-passport? p) - (andmap (λ (s) (string-contains? p s)) required-fields)) - -(count valid-passport? passports) - -;; part 2 -(define passport-fields - (for/list ([p (in-list passports)] #:when (valid-passport? p)) - (~> p string-split (map (curryr string-split ":") _) flatten (apply hash _)))) - -(define (between x low high) - (and (x . >= . low) (x . <= . high))) - -(define (valid-byr? p) - (define year (string->number (hash-ref p "byr"))) - (between year 1920 2002)) - -(define (valid-iyr? p) - (define year (string->number (hash-ref p "iyr"))) - (between year 2010 2020)) - -(define (valid-eyr? p) - (define year (string->number (hash-ref p "eyr"))) - (between year 2020 2030)) - -(define (valid-hgt? p) - (define height (hash-ref p "hgt")) - (cond - [(string-suffix? height "cm") - (let ([h (string->number (string-trim height "cm"))]) (between h 150 193))] - [(string-suffix? height "in") - (let ([h (string->number (string-trim height "in"))]) (between h 59 76))] - [else #false])) - -(define (valid-hcl? p) - (define color (hash-ref p "hcl")) - (regexp-match #px"^#[0-9a-f]{6}$" color)) - -(define (valid-ecl? p) - (member (hash-ref p "ecl") (list "amb" "blu" "brn" "gry" "grn" "hzl" "oth"))) - -(define (valid-pid? p) - (regexp-match #px"^\\d{9}$" (hash-ref p "pid"))) - -(define (valid-stricter-passport? p) - (and (valid-byr? p) - (valid-iyr? p) - (valid-eyr? p) - (valid-hgt? p) - (valid-hcl? p) - (valid-ecl? p) - (valid-pid? p))) - -(count valid-stricter-passport? passport-fields) diff --git a/aoc2020/day-05/day-05.rkt b/aoc2020/day-05/day-05.rkt deleted file mode 100644 index bd89ede..0000000 --- a/aoc2020/day-05/day-05.rkt +++ /dev/null @@ -1,35 +0,0 @@ -#lang racket -(require "../../jj-aoc.rkt" - threading) - -(define tickets - (for/list ([l (in-lines (open-day 5 2020))]) - (~>> l (regexp-match #px"(.{7})(.{3})") rest))) - -(define (find-place str min-p max-p l r) - (if (string=? "" str) - min-p - (let ([p-range (/ (add1 (- max-p min-p)) 2)] [c (substring str 0 1)]) - (cond - [(string=? c l) (find-place (substring str 1) min-p (- max-p p-range) l r)] - [(string=? c r) (find-place (substring str 1) (+ min-p p-range) max-p l r)])))) - -(define (find-row str) - (find-place str 0 127 "F" "B")) -(define (find-col str) - (find-place str 0 7 "L" "R")) - -(define (ticket-id t) - (let ([row (find-row (first t))] [col (find-col (second t))]) (+ col (* 8 row)))) - -;; part 1 -(define occupied-seats - (~>> (for/list ([t (in-list tickets)]) - (ticket-id t)))) - -(apply max occupied-seats) - -;; part 2 -(set-first (set-subtract - (list->set (inclusive-range (apply min occupied-seats) (apply max occupied-seats))) - (list->set occupied-seats))) diff --git a/aoc2020/day-06/day-06.rkt b/aoc2020/day-06/day-06.rkt deleted file mode 100644 index b0e2af9..0000000 --- a/aoc2020/day-06/day-06.rkt +++ /dev/null @@ -1,22 +0,0 @@ -#lang racket -(require "../../jj-aoc.rkt" - threading) - -(define responses (~> (open-day 6 2020) (port->string) (string-split "\n\n"))) - -;; part 1 -(define (response-count-total rs) - (for/sum ([r (in-list rs)]) (~> r (string-replace _ "\n" "") string->list list->set set-count))) - -(response-count-total responses) - -;; part 2 -(define (response-consensus-total rs) - (for/sum ([r (in-list rs)]) - (~> r - (string-split _ "\n") - (map (λ~> string->list list->set) _) - (apply set-intersect _) - set-count))) - -(response-consensus-total responses) \ No newline at end of file diff --git a/aoc2020/day-07/day-07.rkt b/aoc2020/day-07/day-07.rkt deleted file mode 100644 index f2a1ffe..0000000 --- a/aoc2020/day-07/day-07.rkt +++ /dev/null @@ -1,46 +0,0 @@ -#lang racket -(require advent-of-code - threading - rebellion/collection/entry - rebellion/collection/multidict) - -(define raw-rules (~> (open-aoc-input (find-session) 2020 7) (port->string) (string-split "\n"))) - -(define (split-rule r) - (match-define (list head tail) (string-split (string-trim r #px" bags?.") " bags contain ")) - (~>> tail - (regexp-split #px"( bags?,\\s)" _) - (map (λ~> (regexp-match* #px"(\\d+) (\\w+ \\w+)" _ #:match-select rest))) - append* - (map (λ~> (match _ - [(list n c) (list (string->symbol c) (string->number n))] - ['() '()]))) - (cons (string->symbol head) _))) - -(define rules-multidict - (for*/multidict ([ln (in-list raw-rules)] #:do [(match-define (list* from tos) (split-rule ln))] - [to (in-list tos)]) - (entry from to))) - -;; part 1 -(define (bags-that-eventually-contain target) - (for/fold ([holders (set)]) ([rule (in-multidict-entries rules-multidict)]) - (match rule - [(entry outside (list (== target) _)) - (set-union (set-add holders outside) (bags-that-eventually-contain outside))] - [_ holders]))) - -(define part-1 (set-count (bags-that-eventually-contain '|shiny gold|))) -(~a "Part 1: " part-1) -;; (aoc-submit (find-session) 2020 7 1 part-1) - -;; part 2 -(define (bags-that-are-contained-by target) - (for/sum ([holding (in-multidict-entries rules-multidict)]) - (match holding - [(entry (== target) (list held n)) (+ n (* n (bags-that-are-contained-by held)))] - [_ 0]))) - -(define part-2 (bags-that-are-contained-by '|shiny gold|)) -(~a "Part 2: " part-2) -;; (aoc-submit (find-session) 2020 7 1 part-2) diff --git a/aoc2020/day-08/day-08.ipynb b/aoc2020/day-08/day-08.ipynb deleted file mode 100644 index 1cb060b..0000000 --- a/aoc2020/day-08/day-08.ipynb +++ /dev/null @@ -1,220 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Advent of Code 2020\n", - "#### Day 8: Handheld Halting\n", - "\n", - "A series of instructions consisting of jumps, accumulator increments and no-ops has an infinite loop, but changing one no-op to a jump or vice versa will allow it to run to completion.\n", - "\n", - "1. What's the value of the accumulator immediately before the instructions begin to loop?\n", - "2. After fixing the wrong instruction, what's the value of the accumulator at the end of execution?\n", - "\n", - "No surprises in the preamble, just the usual AOC utility functions and the threading macros." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "#lang iracket/lang #:require racket\n", - "(require advent-of-code\n", - " threading)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The instructions are in a text file that looks like\n", - "```\n", - "nop +0\n", - "acc +1\n", - "jmp +4\n", - "acc +3\n", - "jmp -3\n", - "acc -99\n", - "acc +1\n", - "jmp -4\n", - "acc +6\n", - "```\n", - "Since we need to keep track of which instructions to jump to, I've turned it into a hash table so each instruction is indexed starting at 0." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "'((0 acc . 49) (1 jmp . 274) (2 acc . 49) (3 acc . 49) (4 jmp . 476) (5 jmp . 409) (6 jmp . 269) (7 jmp . 1) (8 acc . -11) (9 acc . 5))" - ], - "text/plain": [ - "'((0 acc . 49) (1 jmp . 274) (2 acc . 49) (3 acc . 49) (4 jmp . 476) (5 jmp . 409) (6 jmp . 269) (7 jmp . 1) (8 acc . -11) (9 acc . 5))" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "(define raw-instructions (~> (fetch-aoc-input (find-session) 2020 8) (string-split \"\\n\")))\n", - "\n", - "(define instruction-set\n", - " (for/hash ([instruction (in-list raw-instructions)] [i (in-naturals)])\n", - " (match-define (list op val) (string-split instruction))\n", - " (values i (cons (string->symbol op) (string->number val)))))\n", - "\n", - "(for/list ([i (in-range 10)])\n", - " (cons i (hash-ref instruction-set i)))\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Part 1\n", - "\n", - "Now I write a little interpreter using structural pattern matching and recursion to execute the code.\n", - "* If the program tried to execute an instruction on the line immediately after the last instruction, the program terminates normally. (This won't happen in part 1, but it's important for part 2.)\n", - "* We track the line numbers that have been visited in each step. If a number comes up a second time, that means we're about to start looping, so we break execution here.\n", - "* `acc n` instructions increment the accumulator by `n` and go to the next line.\n", - "* `jmp n` instructions skip up or down `n` instructions.\n", - "* `nop n` instructions don't do anything besides go to the next line." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "'(looping . 1949)" - ], - "text/plain": [ - "'(looping . 1949)" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "(define (execute code [acc 0] [line 0] [visited (set)])\n", - " (match (hash-ref code line 'terminated)\n", - " ['terminated (cons 'terminated acc)]\n", - " [_\n", - " #:when (set-member? visited line)\n", - " (cons 'looping acc)]\n", - " [(cons 'acc n) (execute code (+ acc n) (add1 line) (set-add visited line))]\n", - " [(cons 'jmp n) (execute code acc (+ n line) (set-add visited line))]\n", - " [(cons 'nop _) (execute code acc (add1 line) (set-add visited line))]))\n", - "\n", - "(execute instruction-set)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Part 2\n", - "\n", - "So far so good. Now we're told that flipping exactly one `jmp` to a `nop` or vice versa will fix the code, so let's write a utility function to perform that flip, identify the potential candidates for the fix and get an idea of what our workload will be for this problem." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "305" - ], - "text/plain": [ - "305" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "(define (flip-op op)\n", - " (match op\n", - " [(cons 'jmp n) (cons 'nop n)]\n", - " [(cons 'nop n) (cons 'jmp n)]))\n", - "\n", - "(define instruction-count (hash-count instruction-set))\n", - "(define flippable-bits\n", - " (filter (λ (i) (member (car (hash-ref instruction-set i)) '(jmp nop))) (range instruction-count)))\n", - "\n", - "(length flippable-bits)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "There's only 305 cases to check, so just starting from the top and trying each possible swap in sequence should work fine, rather than trying to come up with some fancier backtracking algorithm. `for/or` stops at the first non-falsy result, so we just have to wait for the first result that matches the `(cons 'terminated _)` pattern." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "'(terminated . 2092)" - ], - "text/plain": [ - "'(terminated . 2092)" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "(for/or ([i (in-list flippable-bits)])\n", - " (define flipped-instruction-set (hash-update instruction-set i flip-op))\n", - " (match (execute flipped-instruction-set)\n", - " [(cons 'looping _) #f]\n", - " [(and success (cons 'terminated _)) success]))\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Racket (trusted)", - "language": "racket", - "name": "racket-trusted" - }, - "language_info": { - "codemirror_mode": "scheme", - "file_extension": ".rkt", - "mimetype": "text/x-racket", - "name": "racket", - "pygments_lexer": "racket", - "version": "8.7" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/aoc2020/day-09/day-09.ipynb b/aoc2020/day-09/day-09.ipynb deleted file mode 100644 index e6f712b..0000000 --- a/aoc2020/day-09/day-09.ipynb +++ /dev/null @@ -1,166 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Advent of Code 2020\n", - "#### Day 9: Encoding Error\n", - "\n", - "In a list of integers, each number after the 25th should be the sum of two of the previous 25 numbers.\n", - "\n", - "1. What's the first number in the list that does not have this property?\n", - "2. The \"encryption weakness\" is the sum of the extrema in a contiguous range of numbers that sums up to the invalid number in part 1. Find the encryption weakness.\n", - "\n", - "I'm using structural pattern matching for this solution, so no extra packages are required beyond the usual." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "vscode": { - "languageId": "racket" - } - }, - "outputs": [], - "source": [ - "#lang iracket/lang #:require racket\n", - "\n", - "(require advent-of-code\n", - " threading)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The data is just a list of integers, so it's straightforward to process." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "vscode": { - "languageId": "racket" - } - }, - "outputs": [], - "source": [ - "(define preamble\n", - " (~> (fetch-aoc-input (find-session) 2020 9) (string-split \"\\n\") (map string->number _)))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "##### Part 1\n", - "\n", - "First, we bind the first 25 values to `head` and the 26th to `x`.\n", - "\n", - "In the `match` syntax, `list-no-order` binds `a` and `b` to the first pair of numbers from anywhere in the first 25 values that satisfies the `#:when` guard. The exact pair doesn't matter; all we need to know is if it's valid and we can move on to the next test.\n", - "\n", - "If nothing satisfies the first clause, we've found our invalid number. We're guaranteed to have an invalid number in the set, so we don't need to guard against `match-define-values` failing when there's fewer than 26 values to work with." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "vscode": { - "languageId": "racket" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "1038347917" - ], - "text/plain": [ - "1038347917" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "(define (find-invalid-number xs)\n", - " (match-define-values (head (list x _ ...)) (split-at xs 25))\n", - " (match head\n", - " [(list-no-order a b _ ...)\n", - " #:when (= x (+ a b))\n", - " (find-invalid-number (rest xs))]\n", - " [_ x]))\n", - "\n", - "(define target-sum (find-invalid-number preamble))\n", - "target-sum" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "##### Part 2\n", - "\n", - "We can find the range with another match statement, this time looking for a sub-list that's at least two elements long and that satisfies the guard. Everything after this is just arithmetic." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "vscode": { - "languageId": "racket" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "137394018" - ], - "text/plain": [ - "137394018" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "(define (find-contiguous-range xs)\n", - " (match xs\n", - " [(list _ ... x ..2 _ ...)\n", - " #:when (= (apply + x) target-sum)\n", - " x]))\n", - "\n", - "(define target-range (find-contiguous-range preamble))\n", - "(+ (apply max target-range) (apply min target-range))" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Racket (trusted)", - "language": "racket", - "name": "racket-trusted" - }, - "language_info": { - "codemirror_mode": "scheme", - "file_extension": ".rkt", - "mimetype": "text/x-racket", - "name": "Racket", - "pygments_lexer": "racket", - "version": "8.7" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/aoc2020/day-10/day-10.rkt b/aoc2020/day-10/day-10.rkt deleted file mode 100644 index 77d9bb7..0000000 --- a/aoc2020/day-10/day-10.rkt +++ /dev/null @@ -1,37 +0,0 @@ -#lang racket - -(require advent-of-code - threading - algorithms - memoize) - -;; part 1 -(define adapters - (~> (fetch-aoc-input (find-session) 2020 10) - (string-split "\n") - (map string->number _) - (sort <) - ((λ (xs) (flatten (list 0 xs (+ 3 (last xs)))))))) - -(~>> adapters - (sliding _ 2) - (map (match-lambda - [(list b a) (- a b)])) - (group-by identity) - (map length) - (apply *)) - -;; part 2 -(define subpaths - (for*/hash ([adapter (in-list adapters)]) - (define predecessor-candidates (inclusive-range (+ 1 adapter) (+ 3 adapter))) - (values adapter (filter (λ (p) (member p adapters)) predecessor-candidates)))) - -(define/memo (find-paths from to) - (define paths (hash-ref subpaths from 'failed)) - (match paths - ['failed 0] - [(list-no-order (== to) _ ...) 1] - [ts (for/sum ([t (in-list ts)]) (find-paths t to))])) - -(find-paths (first adapters) (last adapters)) diff --git a/aoc2020/day-11/day-11.rkt b/aoc2020/day-11/day-11.rkt deleted file mode 100644 index e2fe052..0000000 --- a/aoc2020/day-11/day-11.rkt +++ /dev/null @@ -1,60 +0,0 @@ -#lang racket - -(require advent-of-code) - -(define raw-grid (fetch-aoc-input (find-session) 2020 11)) - -(define/match (parse _) - [(#\L) 'empty] - [(#\#) 'occupied] - [(#\.) 'floor]) - -(define seat-grid - (for*/hash ([(row r) (in-indexed (in-list (string-split raw-grid)))] - [(col c) (in-indexed (in-string row))]) - (values (cons r c) (parse col)))) - -(define (next-seat-state seat state h rule [max-occupy 4]) - (define neighbor-states (rule seat h)) - (match* (state (count (curry eq? 'occupied) neighbor-states)) - [('empty 0) 'occupied] - [('occupied n) - #:when (>= n max-occupy) - 'empty] - [(_ _) state])) - -(define (stabilize h [i 1] #:rule rule #:max-occupy [max-occupy 4]) - (define h* - (for/hash ([(seat state) (in-hash h)]) - (cond - [(eq? state 'floor) (values seat state)] - [else (values seat (next-seat-state seat state h rule max-occupy))]))) - (if (equal? h h*) - (count (curry equal? 'occupied) (hash-values h)) - (stabilize h* (add1 i) #:rule rule #:max-occupy max-occupy))) - -;; part 1 -(define (find-nearest-neighbors p h) - (match-define (cons r c) p) - (for*/list ([r* (in-inclusive-range (sub1 r) (add1 r))] - [c* (in-inclusive-range (sub1 c) (add1 c))] - [p* (in-value (cons r* c*))] - #:unless (equal? p p*)) - (hash-ref h p* 'out-of-bounds))) - -(stabilize seat-grid #:rule find-nearest-neighbors) - -;; part 2 -(define (find-visible-neighbors p h) - (match-define (cons r c) p) - (define directions - (for*/list ([dr '(-1 0 1)] [dc '(-1 0 1)] #:unless (= 0 dr dc)) - (cons dr dc))) - (for/list ([dir (in-list directions)] #:do [(match-define (cons dr dc) dir)]) - (for*/first ([i (in-naturals 1)] - #:do [(define p* (cons (+ r (* i dr)) (+ c (* i dc)))) - (define state (hash-ref h p* 'out-of-bounds))] - #:unless (equal? state 'floor)) - state))) - -(stabilize seat-grid #:rule find-visible-neighbors #:max-occupy 5) diff --git a/aoc2020/day-12/day-12.rkt b/aoc2020/day-12/day-12.rkt deleted file mode 100644 index e4bbd32..0000000 --- a/aoc2020/day-12/day-12.rkt +++ /dev/null @@ -1,56 +0,0 @@ -#lang rackjure - -(require advent-of-code - threading - (only-in relation ->symbol ->number)) - -(struct instruction (direction distance) #:transparent) - -(define (parse-instruction str) - (match str - [(regexp #px"(\\w)(\\d+)" (list _ dir dist)) (instruction (->symbol dir) (->number dist))])) - -(define instructions - (~>> (fetch-aoc-input (find-session) 2020 12) string-split (map parse-instruction))) - -;; part 1 -(struct boat (x y nav) #:transparent) - -(define (angle->direction n) - (case n - [(0) 'E] - [(90) 'N] - [(180) 'W] - [(270) 'S])) - -(define (move-via-direct-command inst b) - (match-define (boat x y facing) b) - (match inst - [(instruction 'N n) (boat x (+ y n) facing)] - [(instruction 'S n) (boat x (- y n) facing)] - [(instruction 'E n) (boat (+ x n) y facing)] - [(instruction 'W n) (boat (- x n) y facing)] - [(instruction 'L n) (boat x y (modulo (+ facing n) 360))] - [(instruction 'R n) (boat x y (modulo (- facing n) 360))] - [(instruction 'F n) (move-via-direct-command (instruction (angle->direction facing) n) b)])) - -(define (find-boat-destination using start instructions) - (match-define (boat x y _) (foldl using start instructions)) - (+ (abs x) (abs y))) - -(find-boat-destination move-via-direct-command (boat 0 0 0) instructions) - -;; part 2 -(define (move-via-waypoint inst b) - (match-define (boat x y (cons wp-x wp-y)) b) - (match inst - [(instruction 'N n) (boat x y (cons wp-x (+ wp-y n)))] - [(instruction 'S n) (boat x y (cons wp-x (- wp-y n)))] - [(instruction 'E n) (boat x y (cons (+ wp-x n) wp-y))] - [(instruction 'W n) (boat x y (cons (- wp-x n) wp-y))] - [(instruction _ 180) (boat x y (cons (- wp-x) (- wp-y)))] - [(or (instruction 'L 90) (instruction 'R 270)) (boat x y (cons (- wp-y) wp-x))] - [(or (instruction 'R 90) (instruction 'L 270)) (boat x y (cons wp-y (- wp-x)))] - [(instruction 'F n) (boat (+ x (* n wp-x)) (+ y (* n wp-y)) (cons wp-x wp-y))])) - -(find-boat-destination move-via-waypoint (boat 0 0 '(10 . 1)) instructions) diff --git a/aoc2020/day-13/day-13.rkt b/aoc2020/day-13/day-13.rkt deleted file mode 100644 index b53f045..0000000 --- a/aoc2020/day-13/day-13.rkt +++ /dev/null @@ -1,32 +0,0 @@ -#lang racket - -(require advent-of-code - (only-in relation ->number) - threading) - -(define (process-ids str) - (~> str (string-split ",") (filter-map (λ (s) (string->number s 10 'number-or-false)) _))) - -(match-define (regexp #px"(\\d+)\n(.+)" (list _ (app ->number timestamp) raw-bus-ids)) - (fetch-aoc-input (find-session) 2020 13)) - -(define bus-ids (process-ids raw-bus-ids)) - -;; part 1 -(for/first ([minute (in-naturals timestamp)] - #:do [(define departing-bus - (for/first ([b bus-ids] #:when (= 0 (remainder minute b))) - b))] - #:when departing-bus) - (* departing-bus (- minute timestamp))) - -;; part 2 -(for/fold ([step 1] [current-timestamp 1] #:result current-timestamp) - ([b* (in-list (string-split (string-trim raw-bus-ids) ","))] - [offset (in-naturals)] - #:unless (equal? b* "x") - #:do [(define bus (->number b*))]) - (values - (* step bus) - (for/first ([n (in-range current-timestamp +inf.0 step)] #:when (= 0 (remainder (+ n offset) bus))) - n))) diff --git a/aoc2020/day-14/day-14.rkt b/aoc2020/day-14/day-14.rkt deleted file mode 100644 index 9ac339c..0000000 --- a/aoc2020/day-14/day-14.rkt +++ /dev/null @@ -1,11 +0,0 @@ -#lang racket - -(require advent-of-code - threading - fancy-app - relation - rebellion/binary/bitstring) - -(define instructions (string-split (fetch-aoc-input (find-session) 2020 14) "\n")) - -(~> (number->string 11 2) ->list (map (->number _) _)) diff --git a/aoc2020/day-15/day-15.rkt b/aoc2020/day-15/day-15.rkt deleted file mode 100644 index 4dd9e88..0000000 --- a/aoc2020/day-15/day-15.rkt +++ /dev/null @@ -1,22 +0,0 @@ -#lang rackjure - -(define first-numbers '(2 20 0 4 1 17)) - -(define number-hash - (for/hash ([(xs i) (in-indexed (drop-right first-numbers 1))]) - (values xs (add1 i)))) - -(define starting-round (~> number-hash hash-values (apply max _) (+ 2))) - -(define (find-spoken-number-at end) - (for/fold ([ns number-hash] [previous-number (last first-numbers)] #:result previous-number) - ([rnd (inclusive-range starting-round end)]) - (define next-spoken-number - (match (ns previous-number) - [#f 0] - [n (- (sub1 rnd) n)])) - (values (ns previous-number (sub1 rnd)) next-spoken-number))) - -(find-spoken-number-at 2020) - -(find-spoken-number-at 30000000) \ No newline at end of file diff --git a/aoc2020/day-16/day-16.rkt b/aoc2020/day-16/day-16.rkt deleted file mode 100644 index 9a38eda..0000000 --- a/aoc2020/day-16/day-16.rkt +++ /dev/null @@ -1,52 +0,0 @@ -#lang racket - -(require racket/struct - advent-of-code - fancy-app - relation - threading - rebellion/base/range) - -(struct field-rule (name range1 range2) #:transparent) - -(define (make-lines strs) - (string-split strs "\n")) -(define (seperate-fields strs) - (~>> (string-split strs ",") (map ->number))) - -(define (process-rules str) - (match str - [(regexp - #px"(.+): (\\d+)-(\\d+) or (\\d+)-(\\d+)" - (list _ name (app ->number min1) (app ->number max1) (app ->number min2) (app ->number max2))) - (field-rule name (closed-range min1 max1) (closed-range min2 max2))])) - -(match-define (list (app (λ~>> make-lines (map process-rules)) ticket-rules) - (app (λ~>> make-lines second seperate-fields) your-ticket) - (app (λ~>> make-lines rest (map seperate-fields)) other-tickets)) - (~> (fetch-aoc-input (find-session) 2020 16 #:cache #true) (string-split "\n\n"))) - -;; part 1 -(define (fails-all-checks? field rules) - (define rule-list (~>> rules (map (λ~> struct->list rest)) flatten)) - (for/and ([rule (in-list rule-list)]) - (not (range-contains? rule field)))) - -(define (ticket-scanning-error-rate tickets rules) - (for*/sum - ([ticket (in-list tickets)] (field (in-list ticket)) #:when (fails-all-checks? field rules)) - field)) - -(ticket-scanning-error-rate other-tickets ticket-rules) - -;; part 2 -(define valid-tickets (filter (ormap (fails-all-checks? _ ticket-rules) _) other-tickets)) - -(define fields (apply map list valid-tickets)) - -(for/list ([field (in-list fields)]) - (for*/list ( - [rule (in-list ticket-rules)] - #:unless (not (or (range-contains? (field-rule-range1 rule) value) - (range-contains? (field-rule-range2 rule) value)))) - (field-rule-name rule))) diff --git a/aoc2021/day-01/day-01.pl b/aoc2021/day-01/day-01.pl deleted file mode 100644 index d3c3fa7..0000000 --- a/aoc2021/day-01/day-01.pl +++ /dev/null @@ -1,20 +0,0 @@ -:- use_module(library(yall)). -:- use_module(library(apply)). - -get_data(Result) :- - setup_call_cleanup(open("day-01/input.txt", read, In), - (read_string(In, _, Str), - split_string(Str, "\n", "\s\t\n", Lines), - maplist(number_string, Result, Lines)), - close(In)). - -calculate_diffs(Result, WindowWidth) :- - get_data(Xs), - length(TrimLeft, WindowWidth), append(TrimLeft, RightSide, Xs), - length(TrimRight, WindowWidth), append(LeftSide, TrimRight, Xs), - maplist([X, Y, Z]>>(Z is Y - X), LeftSide, RightSide, Diffs), - include([X]>>(X > 0), Diffs, Increases), - length(Increases, Result). - -part1_answer(Result) :- calculate_diffs(Result, 1). -part2_answer(Result) :- calculate_diffs(Result, 3). \ No newline at end of file diff --git a/aoc2021/day-01/day-01.rkt b/aoc2021/day-01/day-01.rkt deleted file mode 100644 index 48ef158..0000000 --- a/aoc2021/day-01/day-01.rkt +++ /dev/null @@ -1,20 +0,0 @@ -#lang racket -(require advent-of-code - threading) - -;; part 1 -(define sensor-data - (~> (open-aoc-input (find-session) 2021 1 #:cache (string->path "./cache")) - (port->list read _))) - -(define (count-increases data offset) - (for/sum ([x (in-list data)] - [y (in-list (drop data offset))] - #:when (< x y)) - 1)) - -(~a "Part 1: " (count-increases sensor-data 1)) - -;; part 2 - -(~a "Part 2: " (count-increases sensor-data 3)) \ No newline at end of file diff --git a/aoc2021/day-02/day-02.ex b/aoc2021/day-02/day-02.ex deleted file mode 100644 index d37ab05..0000000 --- a/aoc2021/day-02/day-02.ex +++ /dev/null @@ -1,32 +0,0 @@ -defmodule Day02 do - def part_one(data) do - data - |> Enum.reduce(%{pos: 0, dep: 0}, &method_one/2) - |> get_answer() - end - - def part_two(data) do - data - |> Enum.reduce(%{pos: 0, dep: 0, aim: 0}, &method_two/2) - |> get_answer() - end - - defp method_one({:forward, x}, s), do: %{s | pos: s.pos + x} - defp method_one({:up, x}, s), do: %{s | dep: s.dep - x} - defp method_one({:down, x}, s), do: %{s | dep: s.dep + x} - - defp method_two({:forward, x}, s), do: %{s | pos: s.pos + x, dep: s.dep + s.aim * x} - defp method_two({:up, x}, s), do: %{s | aim: s.aim - x} - defp method_two({:down, x}, s), do: %{s | aim: s.aim + x} - - defp get_answer(s), do: s.pos * s.dep -end - -data = - File.read!("day-02/input.txt") - |> String.split("\n", trim: true) - |> Enum.map(&String.split/1) - |> Enum.map(fn [dir, amt] -> {String.to_atom(dir), String.to_integer(amt)} end) - -Day02.part_one(data) |> IO.inspect() -Day02.part_two(data) |> IO.inspect() diff --git a/aoc2021/day-02/day-02.rkt b/aoc2021/day-02/day-02.rkt deleted file mode 100644 index 0bd0c3d..0000000 --- a/aoc2021/day-02/day-02.rkt +++ /dev/null @@ -1,24 +0,0 @@ -#lang racket -(require advent-of-code - threading - algorithms) - -(define motion-data - (~> (open-aoc-input (find-session) 2021 2 #:cache (string->path "./cache")) - (port->list read _) - (chunks-of _ 2))) - -;; part 1 -(for/fold ([depth 0] [position 0] #:result (* depth position)) ([motion (in-list motion-data)]) - (match motion - [(list 'forward x) (values depth (+ position x))] - [(list 'up x) (values (- depth x) position)] - [(list 'down x) (values (+ depth x) position)])) - -;; part 2 -(for/fold ([aim 0] [depth 0] [position 0] #:result (* depth position)) - ([motion (in-list motion-data)]) - (match motion - [(list 'forward x) (values aim (+ depth (* aim x)) (+ position x))] - [(list 'up x) (values (- aim x) depth position)] - [(list 'down x) (values (+ aim x) depth position)])) diff --git a/aoc2021/day-03/day-03.rkt b/aoc2021/day-03/day-03.rkt deleted file mode 100644 index 95b7efd..0000000 --- a/aoc2021/day-03/day-03.rkt +++ /dev/null @@ -1,39 +0,0 @@ -#lang racket -(require advent-of-code - threading) - -(define data - (~> (open-aoc-input (find-session) 2021 3 #:cache (string->path "./cache")) - port->lines - (map string->list _))) - -;; part 1 -(define most-common-bits - (for*/list ([row (in-list (apply map list data))] [len (in-value (length data))]) - (if (> (count (λ (c) (char=? #\1 c)) row) (/ len 2)) #\1 #\0))) -(define (bit-list->number lst) - (~> lst (apply string _) (string->number _ 2))) - -(define gamma (bit-list->number most-common-bits)) -(define epsilon (~> most-common-bits (map (λ (c) (if (char=? c #\1) #\0 #\1)) _) bit-list->number)) - -(* gamma epsilon) - -;; part 2 -(define (rating-search data comparison) - (for/fold ([candidates data] #:result (bit-list->number (first candidates))) - ([bit (in-list most-common-bits)] [bit-index (in-range 0 (length most-common-bits))]) - #:break (= 1 (length candidates)) - (define keep-bit - (~> candidates - (apply map list _) - (list-ref _ bit-index) - (count (λ (c) (char=? #\1 c)) _) - (comparison _ (/ (length candidates) 2)) - (if _ #\1 #\0))) - (filter (λ (row) (char=? keep-bit (list-ref row bit-index))) candidates))) - -(define oxygen-rating (rating-search data >=)) -(define scrubber-rating (rating-search data <)) - -(* oxygen-rating scrubber-rating) diff --git a/aoc2021/day-04/day-04.rkt b/aoc2021/day-04/day-04.rkt deleted file mode 100644 index c572f74..0000000 --- a/aoc2021/day-04/day-04.rkt +++ /dev/null @@ -1,51 +0,0 @@ -#lang racket -(require advent-of-code - threading - (only-in algorithms chunks-of)) - -(define data - (for/list ([l (in-lines (open-aoc-input (find-session) 2021 4 #:cache (string->path "./cache")))] - #:unless (equal? l "")) - l)) - -(define call-sheet (~> data car (string-split ",") (map string->number _))) -(define bingo-cards - (~> data cdr (map string-split _) (map (λ (row) (map string->number row)) _) (chunks-of 5))) - -(define test-card (first bingo-cards)) - -(define (mark-card card call) - (for/list ([row (in-list card)]) - (for/list ([col (in-list row)]) - (if (eq? col call) 'X col)))) - -(define (check-card card) - (for/or ([row (in-sequences card (apply map list card))]) - (equal? row '(X X X X X)))) - -(define (multiply-by-last-call n call) - (match n - ['X 0] - [n (* n call)])) - -(define (evaluate-cards cards calls [check (curry ormap check-card)] [exception not]) - (for/fold ([current-cards cards] - [last-call 0] - #:result (~> current-cards - (findf check-card _) - flatten - (map (λ (n) (multiply-by-last-call n last-call)) _) - (apply + _))) - ([call (in-list calls)]) - #:break (check current-cards) - (values (for/list ([card (in-list current-cards)] #:unless (exception card)) - (mark-card card call)) - call))) - -;; part 1 -(evaluate-cards bingo-cards call-sheet) -;; part 2 -(evaluate-cards bingo-cards - call-sheet - (λ (cards) (and (= (length cards) 1) (check-card (first cards)))) - check-card) diff --git a/aoc2021/day-05/day-05.rkt b/aoc2021/day-05/day-05.rkt deleted file mode 100644 index e568490..0000000 --- a/aoc2021/day-05/day-05.rkt +++ /dev/null @@ -1,57 +0,0 @@ -#lang racket -(require advent-of-code - threading) - -(define data - (for/list ([l (in-lines (open-aoc-input (find-session) 2021 5 #:cache (string->path "./cache")))]) - (~> l (string-replace " -> " ",") (string-split ",") (map string->number _)))) - -(define (trace-line! x y vec) - (define linear-coord (+ y (* 1000 x))) - (vector-set! vec linear-coord (+ 1 (vector-ref vec linear-coord)))) - -(define/match (orthogonal? coord-pair) - [((or (list n _ n _) (list _ n _ n))) #t] - [(_) #f]) - -(define-values (orthogonal-lines diagonal-lines) (partition orthogonal? data)) - -(define (dir a b) - (if (< a b) 1 -1)) - -(define (trace-orthogonal! coord-pairs tracer result) - (for ([coord-pair (in-list coord-pairs)]) - (match coord-pair - [(list x y1 x y2) - (for ([y (inclusive-range y1 y2 (dir y1 y2))]) - (tracer x y result))] - [(list x1 y x2 y) - (for ([x (inclusive-range x1 x2 (dir x1 x2))]) - (tracer x y result))]))) - -(define (trace-diagonal! coord-pairs tracer result) - (for ([coord-pair (in-list coord-pairs)]) - (match-define (list x1 y1 x2 y2) coord-pair) - (for ([x (inclusive-range x1 x2 (dir x1 x2))] [y (inclusive-range y1 y2 (dir y1 y2))]) - (tracer x y result)))) - -;; part 1 -(define sea-floor (make-vector 1000000)) -(trace-orthogonal! orthogonal-lines trace-line! sea-floor) -(vector-count (curry <= 2) sea-floor) - -;; part 2 -;; since the orthogonal lines have already been traced, -;; all I need to do is add the diagonal ones to the existing vector -(trace-diagonal! diagonal-lines trace-line! sea-floor) -(vector-count (curry <= 2) sea-floor) - -;; alternate sparse representation -(define (trace-line-sparse! x y dict) - (hash-update! dict (list x y) (curry + 1) 0)) - -(define sea-floor-dict (make-hash)) -(trace-orthogonal! orthogonal-lines trace-line-sparse! sea-floor-dict) -(count (curry <= 2) (hash-values sea-floor-dict)) -(trace-diagonal! diagonal-lines trace-line-sparse! sea-floor-dict) -(count (curry <= 2) (hash-values sea-floor-dict)) diff --git a/aoc2021/day-06/day-06.ex b/aoc2021/day-06/day-06.ex deleted file mode 100644 index efe10e4..0000000 --- a/aoc2021/day-06/day-06.ex +++ /dev/null @@ -1,35 +0,0 @@ -defmodule Day06 do - def next_day(state) do - with one_day_older <- Enum.into(state, %{}, fn {k, v} -> {k - 1, v} end), - {n, s} <- Map.pop(one_day_older, -1, 0) do - Map.update(s, 6, n, &(&1 + n)) - |> Map.put(8, n) - end - end -end - -school = - with {:ok, data} <- File.read("input.txt") do - data - |> String.trim() - |> String.split(",") - |> Enum.map(&String.to_integer/1) - end - -starting_state = Enum.frequencies(school) - -Enum.reduce( - Enum.to_list(1..80), - starting_state, - fn _, acc -> Day06.next_day(acc) end -) -|> Enum.reduce(0, fn {_, v}, acc -> v + acc end) -|> IO.inspect() - -Enum.reduce( - Enum.to_list(1..256), - starting_state, - fn _, acc -> Day06.next_day(acc) end -) -|> Enum.reduce(0, fn {_, v}, acc -> v + acc end) -|> IO.inspect() diff --git a/aoc2021/day-06/day-06.livemd b/aoc2021/day-06/day-06.livemd deleted file mode 100644 index 5ab794f..0000000 --- a/aoc2021/day-06/day-06.livemd +++ /dev/null @@ -1,152 +0,0 @@ - - - -# Advent of Code 2021, Day 6 - -## Short problem summary - -A school of fish reproduce according to the following rules: - -* Every fish has an "internal timer" -* The timer decrements by one every day -* If the timer is at 0, the timer is instead reset to 6, - and a new fish with an internal timer of 8 is added to the school - -Questions: - -1. How many fish are in the school after 80 days? -2. How many fish are in the school after 256 days? - -## Setting up - -The initial input is a list of fish, represented by the initial value of their internal timer: - -```elixir -school = - with {:ok, data} <- File.read("day-06/input.txt") do - data - |> String.trim() - |> String.split(",") - |> Enum.map(&String.to_integer/1) - end -``` - -```output -[5, 4, 3, 5, 1, 1, 2, 1, 2, 1, 3, 2, 3, 4, 5, 1, 2, 4, 3, 2, 5, 1, 4, 2, 1, 1, 2, 5, 4, 4, 4, 1, 5, - 4, 5, 2, 1, 2, 5, 5, 4, 1, 3, 1, 4, 2, 4, 2, 5, 1, ...] -``` - -Every fish with the same starting internal timer will reproduce at the same time, -as will all of the children of those fish and their children, and so forth, -so we don't need to track individual fish; we just need to group the fish based on -their starting internal timer and track those groups throughout the simulation. - -```elixir -starting_state = Enum.frequencies(school) -``` - -```output -%{1 => 88, 2 => 45, 3 => 54, 4 => 52, 5 => 61} -``` - -Every time a day passes, the following things happen: - -* All the internal timers decrement by 1 -* The group of fish with an internal timer of -1 is reset to 6 - (added to any existing fish whose timers are already at 6), - and an equal-sized group of fish with internal timer 8 is added - -```elixir -defmodule Day06 do - def next_day(state) do - with one_day_older <- Enum.into(state, %{}, fn {k, v} -> {k - 1, v} end), - {n, s} <- Map.pop(one_day_older, -1, 0) do - Map.update(s, 6, n, &(&1 + n)) - |> Map.put(8, n) - end - end -end - -day1 = Day06.next_day(starting_state) -``` - -```output -%{0 => 88, 1 => 45, 2 => 54, 3 => 52, 4 => 61, 6 => 0, 8 => 0} -``` - -After the first day there's not any fish old enough to reproduce yet, but after the second day, - -```elixir -day2 = Day06.next_day(day1) -``` - -```output -%{0 => 45, 1 => 54, 2 => 52, 3 => 61, 5 => 0, 6 => 88, 7 => 0, 8 => 88} -``` - -The 88 fish whose timers were at 0 have rolled over to 6 and created 88 more fish with timers at 8. - -## Solution - -Now we just need to apply the transformation function the necessary number -of times and sum up the total population in the end: - -```elixir -part1_state = - Enum.reduce( - Enum.to_list(1..80), - starting_state, - fn _, acc -> Day06.next_day(acc) end - ) - |> IO.inspect() - |> Enum.reduce(0, fn {_, v}, acc -> v + acc end) -``` - -```output -%{ - 0 => 24572, - 1 => 43660, - 2 => 30525, - 3 => 48458, - 4 => 41318, - 5 => 47697, - 6 => 57731, - 7 => 23218, - 8 => 33738 -} -``` - -```output -350917 -``` - -Identically for part 2, - -```elixir -part2_state = - Enum.reduce( - Enum.to_list(1..256), - starting_state, - fn _, acc -> Day06.next_day(acc) end - ) - |> IO.inspect() - |> Enum.reduce(0, fn {_, v}, acc -> v + acc end) -``` - -```output -%{ - 0 => 139170477178, - 1 => 162618979933, - 2 => 169389497028, - 3 => 188231720546, - 4 => 207908029672, - 5 => 217769615201, - 6 => 252681772250, - 7 => 117023886952, - 8 => 138124736869 -} -``` - -```output -1592918715629 -``` diff --git a/aoc2021/day-06/day-06.rkt b/aoc2021/day-06/day-06.rkt deleted file mode 100644 index d8855ba..0000000 --- a/aoc2021/day-06/day-06.rkt +++ /dev/null @@ -1,27 +0,0 @@ -#lang racket -(require advent-of-code - list-utils - threading - racket/hash) - -(define fish-data - (~> (open-aoc-input (find-session) 2021 6 #:cache (string->path "./cache")) - port->string - string-trim - (string-split ",") - (map string->number _))) - -(define (simulate-fish time-period) - (for/fold ([state (frequencies fish-data)] #:result (~> state hash-values (apply + _))) - ([day (inclusive-range 1 time-period)]) - (define day-older-fish - (for/hash ([(days pop) (in-hash state)]) - (values (sub1 days) pop))) - (define breeding-fish (hash-ref day-older-fish -1 0)) - (hash-union (hash-remove day-older-fish -1) (hash 8 breeding-fish 6 breeding-fish) #:combine +))) - -;; part 1 -(simulate-fish 80) - -;; part 2 -(simulate-fish 256) diff --git a/aoc2021/day-06/input.txt b/aoc2021/day-06/input.txt deleted file mode 100644 index ba3c3cc..0000000 --- a/aoc2021/day-06/input.txt +++ /dev/null @@ -1 +0,0 @@ -5,4,3,5,1,1,2,1,2,1,3,2,3,4,5,1,2,4,3,2,5,1,4,2,1,1,2,5,4,4,4,1,5,4,5,2,1,2,5,5,4,1,3,1,4,2,4,2,5,1,3,5,3,2,3,1,1,4,5,2,4,3,1,5,5,1,3,1,3,2,2,4,1,3,4,3,3,4,1,3,4,3,4,5,2,1,1,1,4,5,5,1,1,3,2,4,1,2,2,2,4,1,2,5,5,1,4,5,2,4,2,1,5,4,1,3,4,1,2,3,1,5,1,3,4,5,4,1,4,3,3,3,5,5,1,1,5,1,5,5,1,5,2,1,5,1,2,3,5,5,1,3,3,1,5,3,4,3,4,3,2,5,2,1,2,5,1,1,1,1,5,1,1,4,3,3,5,1,1,1,4,4,1,3,3,5,5,4,3,2,1,2,2,3,4,1,5,4,3,1,1,5,1,4,2,3,2,2,3,4,1,3,4,1,4,3,4,3,1,3,3,1,1,4,1,1,1,4,5,3,1,1,2,5,2,5,1,5,3,3,1,3,5,5,1,5,4,3,1,5,1,1,5,5,1,1,2,5,5,5,1,1,3,2,2,3,4,5,5,2,5,4,2,1,5,1,4,4,5,4,4,1,2,1,1,2,3,5,5,1,3,1,4,2,3,3,1,4,1,1 diff --git a/aoc2021/day-07/day-07.rkt b/aoc2021/day-07/day-07.rkt deleted file mode 100644 index 89d5009..0000000 --- a/aoc2021/day-07/day-07.rkt +++ /dev/null @@ -1,28 +0,0 @@ -#lang racket -(require advent-of-code - threading - math/statistics) - -(define crab-data - (~> (open-aoc-input (find-session) 2021 7 #:cache #t) - port->string - string-trim - (string-split ",") - (map string->number _))) - -(define (gauss-sum n) - (/ (* n (+ n 1)) 2)) -(define (compute-fuel-use f crabs align-to) - (for/sum ([crab (in-list crabs)]) (f (abs (- crab align-to))))) - -;; using the fact that the optimum location is at the median -;; of the crabs' starting location for the linear relationship -;; and at a coordinate within 1 unit of the mean for the quadratic one - -(~>> crab-data (median <) (compute-fuel-use identity crab-data)) - -(~>> crab-data - mean - ((λ (m) (list (floor m) (ceiling m)))) - (map (curry compute-fuel-use gauss-sum crab-data)) - (apply min)) diff --git a/aoc2021/day-08/day-08.rkt b/aoc2021/day-08/day-08.rkt deleted file mode 100644 index 6476eae..0000000 --- a/aoc2021/day-08/day-08.rkt +++ /dev/null @@ -1,64 +0,0 @@ -#lang racket -(require threading - list-utils - "../../jj-aoc.rkt") - -(struct trial-data (signal output) #:transparent) - -(define (string->sets s) - (~> s string-split (map (λ~> string->list list->set) _))) - -(define data - (for/list ([l (in-lines (open-day 8))] #:unless (equal? l "")) - (~> l (string-split _ " | ") (map string->sets _) (apply trial-data _)))) - -;; part 1 -(for*/sum ([trial (in-list data)] [output (in-list (trial-data-output trial))] - #:when (ormap (λ~> (= (set-count output))) '(2 3 4 7))) - 1) - -;; part 2 -(define (matching-pattern len trial) - (define solution-set - (for*/list ([signal (in-list (trial-data-signal trial))] #:when (= (set-count signal) len)) - signal)) - (match solution-set - [(list s) s] - [s (apply set-intersect s)])) - -(define (determine-arrangement t) - (let* ([pattern-1 (matching-pattern 2 t)] - [pattern-4 (matching-pattern 4 t)] - [pattern-7 (matching-pattern 3 t)] - [pattern-8 (matching-pattern 7 t)] - [pattern-shared-235 (matching-pattern 5 t)] - [pattern-3 (set-union pattern-1 pattern-shared-235)] - [pattern-9 (set-union pattern-4 pattern-shared-235)] - [pattern-shared-069 (matching-pattern 6 t)] - [pattern-just-f (set-subtract pattern-shared-069 pattern-shared-235)] - [pattern-just-e - (set-subtract pattern-8 (set-union pattern-4 pattern-shared-235 pattern-shared-069))] - [pattern-2 (set-union (set-subtract pattern-3 pattern-just-f) pattern-just-e)] - [pattern-just-c (set-subtract (set-intersect pattern-4 pattern-7) pattern-just-f)] - [pattern-6 (set-subtract pattern-8 pattern-just-c)] - [pattern-5 (set-subtract pattern-6 pattern-just-e)] - [pattern-0 (set-union (set-subtract pattern-8 pattern-shared-235) pattern-shared-069)]) - (~> (list pattern-0 - pattern-1 - pattern-2 - pattern-3 - pattern-4 - pattern-5 - pattern-6 - pattern-7 - pattern-8 - pattern-9) - enumerate - make-hash))) - -(for/sum ([trial (in-list data)]) - (~>> trial - trial-data-output - (map (λ~>> (hash-ref (determine-arrangement trial)))) - (apply ~a) - string->number)) diff --git a/aoc2021/day-09/day-09.livemd b/aoc2021/day-09/day-09.livemd deleted file mode 100644 index 3b984a5..0000000 --- a/aoc2021/day-09/day-09.livemd +++ /dev/null @@ -1,138 +0,0 @@ - - - -# Advent of Code 2021, Day 9 - -## Short problem summary - - - -**Part 1.** Find the total "risk level" of all the local minima on a relief map, represented by a -100 $\times$ 100 array of integers from 1 to 9. Only orthogonal neighbors count. -The risk level is the elevation plus one. - -**Part 2.** Find the product of the areas of the three largest basins on the relief map. Basins are regions -bordered by the edges of the map or by points with elevation 9. -Again, only orthogonal neighbors count. - -## Setup - -I'm using [Nx](https://github.com/elixir-nx/nx/tree/main/nx#readme) tensors -since this problem will require a lot of arbitrary indexing. - -```elixir -Mix.install([ - {:nx, "~> 0.1.0-dev", github: "elixir-nx/nx", sparse: "nx", override: true} -]) -``` - -```output -:ok -``` - -Bringing in the data as a new 2D tensor: - -```elixir -floor = - with {:ok, data} <- File.read("input.txt") do - data - |> String.trim() - |> String.split("\n") - |> Enum.flat_map(&String.graphemes/1) - |> Enum.map(&String.to_integer/1) - |> Enum.chunk_every(100) - |> Nx.tensor(names: [:y, :x]) - end -``` - -```output -#Nx.Tensor< - s64[y: 100][x: 100] - [ - [7, 6, 5, 9, 9, 9, 1, 0, 9, 8, 9, 9, 9, 8, 7, 6, 5, 7, 9, 9, 1, 0, 1, 2, 9, 8, 7, 9, 9, 9, 9, 8, 7, 6, 4, 3, 2, 1, 2, 3, 4, 5, 9, 8, 7, 4, 3, 4, 5, 5, ...], - ... - ] -> -``` - -## Part 1 - -For a given coordinate $(x, y)$, we want to examine its orthogonal neighbors, -but only the ones that exist within the map. - -```elixir -defmodule Day09.Part1 do - def neighbors({x, y}) do - [{x + 1, y}, {x - 1, y}, {x, y + 1}, {x, y - 1}] - |> Enum.filter(fn {x, y} -> x >= 0 && x < 100 && y >= 0 && y < 100 end) - end -end -``` - -```output -{:module, Day09.Part1, <<70, 79, 82, 49, 0, 0, 7, ...>>, {:neighbors, 1}} -``` - -Now scan the whole array to check each cell's neighbors, -and return the "risk level" for each local minimum, then accumulate the sum. - -```elixir -risk_level = - for x <- 0..99, - y <- 0..99, - reduce: 0 do - acc -> - Day09.Part1.neighbors({x, y}) - |> Enum.all?(fn {xn, yn} -> floor[x][y] < floor[xn][yn] end) - |> if(do: acc + Nx.to_number(floor[x][y]) + 1, else: acc) - end -``` - -```output -591 -``` - -## Part 2 - -Now we need to recursively walk outwards from each previously-unwalked point -until we can't walk any further. An agent will keep track of the visited points. - -```elixir -defmodule Day09.Part2 do - def walkable?({x, y} = coords, tensor, pid) do - Nx.to_number(tensor[x][y]) < 9 && not Agent.get(pid, fn m -> Map.has_key?(m, coords) end) - end - - def walk_it(coords, tensor, pid) do - if walkable?(coords, tensor, pid) do - Agent.update(pid, fn m -> Map.put(m, coords, true) end) - - for c <- Day09.Part1.neighbors(coords) do - walk_it(c, tensor, pid) - end - |> Enum.reduce(1, fn x, acc -> acc + x end) - else - 0 - end - end -end -``` - -```output -{:module, Day09.Part2, <<70, 79, 82, 49, 0, 0, 11, ...>>, {:walk_it, 3}} -``` - -```elixir -{:ok, tracker} = Agent.start_link(fn -> %{} end) - -for x <- 0..99, y <- 0..99 do - Day09.Part2.walk_it({x, y}, floor, tracker) -end -|> Enum.sort(:desc) -|> Enum.take(3) -|> Enum.reduce(fn x, acc -> acc * x end) -``` - -```output -1113424 -``` diff --git a/aoc2021/day-09/day-09.rkt b/aoc2021/day-09/day-09.rkt deleted file mode 100644 index d550a9e..0000000 --- a/aoc2021/day-09/day-09.rkt +++ /dev/null @@ -1,59 +0,0 @@ -#lang racket - -(require threading - "../../jj-aoc.rkt") - -(define sea-floor-data - (for/vector ([l (in-lines (open-day 9))] #:unless (equal? l "")) - (~>> l string->list (map (λ~>> ~a string->number)) list->vector))) - -(define max-rows (vector-length sea-floor-data)) -(define max-cols (vector-length (vector-ref sea-floor-data 0))) -(define-values (min-rows min-cols) (values 0 0)) - -(define (vector2d-ref vec coord) - (match-define `(,r ,c) coord) - (~> vec (vector-ref r) (vector-ref c))) - -(define (adjacent coords) - (match-define `(,r ,c) coords) - `((,(add1 r) ,c) (,(sub1 r) ,c) (,r ,(add1 c)) (,r ,(sub1 c)))) - -(define (valid-coordinate coord) - (match-define `(,r ,c) coord) - (and (>= r min-rows) (< r max-rows) (>= c min-cols) (< c max-cols))) - -;; part 1 -(define (lowest-point? vec coord) - (for*/and ([neighbor (in-list (adjacent coord))] #:when (valid-coordinate neighbor)) - (< (vector2d-ref vec coord) (vector2d-ref vec neighbor)))) - -(for*/sum ([r (in-range min-rows max-rows)] [c (in-range min-cols max-cols)] - [coord (in-value `(,r ,c))] - #:when (lowest-point? sea-floor-data coord)) - (add1 (vector2d-ref sea-floor-data coord))) - -;; part 2 -;; all the basins are bordered by the edges or by ridges of elevation 9, -;; so it's not really necessary to start at a low point -(define walked (make-hash)) - -(define (walkable? vec coord) - (and (< (vector2d-ref vec coord) 9) (not (hash-has-key? walked coord)))) - -(define (walk-the-basin vec coord) - (cond - [(walkable? vec coord) - (hash-set! walked coord 'visited) - (add1 (for/sum [(neighbor (in-list (adjacent coord))) #:when (valid-coordinate neighbor)] - (walk-the-basin vec neighbor)))] - [else 0])) - -(define basins - (for*/list ([r (in-range min-rows max-rows)] - [c (in-range min-cols max-cols)] - [coord (in-value `(,r ,c))] - #:when (walkable? sea-floor-data coord)) - (walk-the-basin sea-floor-data coord))) - -(~> basins (sort >) (take 3) (apply * _)) diff --git a/aoc2021/day-09/input.txt b/aoc2021/day-09/input.txt deleted file mode 100644 index 322b31f..0000000 --- a/aoc2021/day-09/input.txt +++ /dev/null @@ -1,100 +0,0 @@ -7659991098999876579910129879999876432123459874345567890126678999876588975767899323456989767899432101 -8998789987898765467891239868899876541012398765123456789234567897643467894656798912399878948678944212 -9867678976789878598954398756789997632343459873234569898765679999856578943547987893989865434567894323 -7654567895896989679767499847994398755456579987656778969876892198767989652129876889876976725678965734 -8767678954345698799898987659943219876577694598787889545987931019879996543298965679965987438789876799 -9898789921239799898989899798794399987688965679898993534598942123989987654987654567894596549899989987 -4969999892398989987876789987689989998789898789979992123989653934598798965976543678943987678999999876 -3459898789497878976545678986567878999895679898768989239879869896789659879865432469432198799998789765 -2498765699976567995434389765434567899934989977655679356965998789896545989865321258921019999987678954 -3987654398765456789321238979325456799329898766434798999876797698987432198754310347894329789876567893 -4696543219876967998910147998214345678998789954323987689999977567898543479885541456789498678988678932 -5987654423987878987521236987601234567891678893219876567898765479987654567976632367899987569899789321 -6798767834698989997432345698524568698932456789398765466989876567898765689987545478959768456789893210 -7899898945679499876545656997434578799843569892987654345678989679999876799798658569349654367878964322 -8977999498789398987896769876545689998767678931999869656789698789999987987698767895498743212567895433 -9656789329898987899929879987676790199898989949877998767896559899878998977569978999987654323458986654 -8797995434987896989434989798989891987919499896765569888921434998767789865452989998998795434569997765 -9979899549876785678945995639698999876329398764354456999990125899545678954321299987899987895678949878 -9865678998765434567899894324567892985498999863212347898989436798434599876210389996789999998789434999 -7654567899896647978989789212459921296987899954393478987678945987324879965341567895679891019998959865 -8543878901987656789765678901268933459876999895989569658567959876412568897432467894599789923987898654 -9212389919898767897654577892999545998765798789878978945456899954323456789576578923989679899876789543 -9543457898769879986543456789889959876543987676567899432345789976436567897697989219878565678965678932 -8754568987654989765432347898767899989432976543456964321012498987545679998989892109867434699764567891 -9867689298543299986541034987656789998743989654677895632134987898696789989878789298754324789543488989 -1978792129654569987732129874543567897654799965789976853239876799989999878765678999865455697601245678 -0989893098765678998653298763212375789795679878994989966398754889879898765464569899976566789212346789 -9898954239987789998784987654301234699989789989873398765459765678968789884323456789987677894323456898 -8777895345698999899895698976214345789878999997762129876589896789345678965434567999898989976564568967 -7656976457899019767987899765423456789569899876543299987678987891234569876545898998769394987875679456 -6546899568992198657898939878534677893456789987654989898789398932347678999856789889843212398986989345 -5435688979879932545679929989665788902569991298779878789899299543458789798767895678932101569997890123 -4323567899767891434589898998776899543457899999898768655978987654569899679878934569643219878998921254 -6764579987658910123456797879887899656568978789987653234567998785678998532989545678954399989999432765 -7875689998767891234567896569998998967989767698798767145678999896789987643498756789765989992987543876 -8976796899878932545678997998769467899899854599659898657789985959897898784569867899876978931098954987 -9697895799989873467889989999652348999799965678943939768999664543956789895679878979989867892129895698 -4598954569899964578999878987643499997689978789432129879998543212345678976989989459899756789298789789 -3499543798798765699998767898984987843579899996583235989987632105468789987899992398789898999987678991 -4985432987679876789999658989876986532498798889874346798798743236589893498989891987678989459986568990 -9876521296567987899876545878989875421987656778965498987669654587679999999876789898547678998765456789 -9876432987678998998765434569998767410996545567896569896556965689789987898865676789435589765432369999 -3987543498799549769886325679987654329877434456789698789439879789899976987764545678923459879321287899 -4599656789989432458997212568999769498764321298999987688956989897999899876743236789212398998932456998 -9798767897678921367989323459998998999875210147899876567897891956789798765432145794301987897893569997 -8999878996569432349876534569987687898654321236789767456789932345679659976545012689419876896789878986 -7899989987458943499998765678976546789985434545678954345679543567789545987983234568998765645678989565 -6789199654347896589989876789987434579876545758789543237789654579895434499874356789329874234567893434 -5679298789756789678976987899874323459987859767899654345678965989965421298765667895499932125689932123 -4568999898969898789765698998765212398498869878998765656789879898965432399876878976987891034567894034 -3456789987898999899894329987654323987349978989999898767896999797896743987987899989896789123678985125 -2346899876767892999989212398765499876567989999899959878934987676989659876798945698765695434679876789 -1256998765458993498979903459876989987698999999789943989949898455678998765759899987654989545689989899 -4349879876569989986567894967989878999789989897678892099898789334569987674545678998743478957897696999 -5478968987678979765456999898998767879899876789546789298788699212989876543234567998932569768998565678 -6568956998789569876567897789999857867998975695437995987657598909898995432123459886521678989895434567 -7678939879892458987678956699896645456987764789567894696543467898767986321019598765432789496789323878 -8789998765901456798789545598775435349876543999698943495432356789656997432198969876547892345689439989 -9895987654312345789899434459654324234987875898789212989321234896549876545997656989856921234579998997 -6954398767433566789998921398743210125698986789894309878540145789432997859876543398767890123467897686 -5695999876544678998787892987654341234589987895999498765431236894320989767987432129898921294878987575 -4989899987698789987656789398765492395678998934998989896549898965999878979876421012969939989999098464 -2976789998789899876543789249876989989989239019887678987656789879878767898985432123457898679889198323 -9895678999894968987654590123989879978990129198764569998987894998765459987899543434568987546778987634 -8654547899923656798985891294598768969893298998765678999598912349821398795698976546699876435569876545 -6543336789012349899876789989987656756789987899976789989499909496543989654567898687987665523456987676 -7652125978923598987987899878996541234567896569899899978987898987859878965678998789876563212345698787 -6543234567894987876798998769876532345978943456799998767896987598998969898799989898765432101234569898 -7685346878999876765689987655989645567899212566778987658965398459987856789899978999876543212385789999 -8876798989998765434569976743498756789956401234568998789876999349876546899988769899987654525678999999 -9987899697989954323798765632359867899543212345679239899989899956997635789879656789999769434789898989 -4599996546567893212987654321235978998764637566989139999998789899986523998967545698999898945699787678 -3499989435457899433498765435348989689895547677891098988997655798765439897645634567899976896789676567 -2989978921345678994569876546757894578987678788992987677893234569898698765430125698999865689896543456 -9879868935458789989778987987868943989998989999789987566989123456989789876321234789997684578965432123 -8965657899599999878989998999979659899879596545678976435778934569879894987432545679876543467896673294 -7654545698989997569999999998989799789965432434789897324567895979964933498545789789987432456998784989 -6543234987978965478999899987899987656974321023498788212379976798763212379676899899997544567899999878 -5432129876568896567898789656789976549875432164589654323568987987654343456987895978999655778956798967 -4321019987456789678987698943495987678987543765678965454569998998765454567898954567898776789345987954 -5432998765345698989996587992976798789798765897789876875678999869876569878979943459979987893212986543 -6549879876234767899985456789897899997659897899897987996789998754997878989767892598965398994323497632 -7698765438123456789876323496789992198943998943956998987899987643298989997656999987890139989434598745 -8899954321014567899865212345678989989894989012345899498989998759109499998747988976789239878965987656 -9998765732125678999954345489789679878789876543456789349678939998912349876434567895678949867896798767 -4349876653346789998769497679898998765699998754678991234569019887893498765325658934589998758659999878 -4239999778659899899898989989967987854897899869789893965678998766799987654312349898699896549237899989 -9398999889767998789987978995459876543786789878898789896799987655678998895401256789798765432126789997 -8987899999879987678976567894398765432645699989997678789929876543767899986212868999899654321034599896 -7856789432989876569875456894298764321237989899986565678910987432347678997343479756998789532123456789 -6545678921098765454986587932129879854349876799867434568924596541234599987654569545689898743454567895 -5436889932129854323697698943234988765698765987654523567895987762345789598968678934578999654765678954 -4321968894298765634598789954445699878987654599763212348997898943579893459899789545678998765878789875 -5872456789349878745679897895768789989877653459894103456789999874568932598789897676789129878989899986 -8763878995467989856789976979879899998765432345989294567899899865679943987699998989899234989299999899 -7654567896568995977891234568999978919876521239878989678956756978989894976568999195978976790199899788 -8765698987878923988910123456789567923987432399765679789432347989998769876456789234567897892988698677 -9878789498999219899321256587995456894898643989813478997643458994987656987567894345679998999876545556 -2989893219879998765432347898932345995798759876524567899856969543499897897698987457989899298765434345 -1099954523467899876545456999545476789899899976435788923987897652101998998789876567897654349854321237 diff --git a/aoc2021/day-10/day-10.rkt b/aoc2021/day-10/day-10.rkt deleted file mode 100644 index ea1b389..0000000 --- a/aoc2021/day-10/day-10.rkt +++ /dev/null @@ -1,57 +0,0 @@ -#lang racket - -(require math/statistics - threading - "../../jj-aoc.rkt") - -(define chunks (port->lines (open-day 10 2021))) - -(define (opening-bracket? c) - (member c (string->list "([{<"))) - -(define (matching-brackets? c-left c-right) - (member (string c-left c-right) '("()" "[]" "{}" "<>"))) - -(define (parse-brackets lst [acc '()]) - (cond - [(empty? lst) acc] - [(opening-bracket? (first lst)) (parse-brackets (rest lst) (cons (first lst) acc))] - [(matching-brackets? (first acc) (first lst)) (parse-brackets (rest lst) (rest acc))] - [else (get-score (first lst))])) - -;; part 1 -(define (get-score c) - (match (string c) - [")" 3] - ["]" 57] - ["}" 1197] - [">" 25137])) - -(define (score-invalid-string chunk) - (match (parse-brackets (string->list chunk)) - [(? list?) 0] - [n n])) - -(for/sum ([chunk (in-list chunks)]) (score-invalid-string chunk)) - -;; part 2 -(define (completion-score lst) - (for/fold ([score 0]) ([c (in-list lst)]) - (define val - (match (string c) - ["(" 1] - ["[" 2] - ["{" 3] - ["<" 4])) - (+ (* 5 score) val))) - -(define (score-incomplete-string chunk) - (match (parse-brackets (string->list chunk)) - [(? list? lst) (completion-score lst)] - [n 0])) - -(~>> (for*/list ([chunk (in-list chunks)] - [score (in-value (score-incomplete-string chunk))] - #:when (> score 0)) - score) - (median <)) diff --git a/aoc2021/day-11/day-11.rkt b/aoc2021/day-11/day-11.rkt deleted file mode 100644 index bc22991..0000000 --- a/aoc2021/day-11/day-11.rkt +++ /dev/null @@ -1,56 +0,0 @@ -#lang racket -(require "../../jj-aoc.rkt" - threading) - -(define coords - (~>> (for/list ([r (in-range 10)]) - (for/list ([c (in-range 10)]) - (cons r c))) - (apply append))) - -(define octopus-data - (~>> (for/list ([l (in-lines (open-day 11 2021))] #:unless (equal? l "")) - (~>> l string->list (map (λ~>> ~a string->number)))) - (apply append) - (map cons coords) - make-hash)) - -(define total-length (hash-count octopus-data)) -(define row-length (sqrt total-length)) - -(define (adjacent-to coord) - (match-define (cons r c) coord) - (for*/list ([row (in-list '(-1 0 1))] [col (in-list '(-1 0 1))] #:unless (= 0 row col)) - (cons (+ r row) (+ c col)))) - -(define (simulate-octopi-step data) - (define flashed-this-step (mutable-set)) - - (let look-for-more-flashes ([octopi (for/hash ([(k v) data]) - (values k (add1 v)))] - [flashes-so-far 0]) - (define-values (next-octopus-update flashes-this-update) - (for*/fold ([octopi octopi] [flashes 0]) - ([(p x) (in-hash octopi)] #:when (> x 9) #:unless (set-member? flashed-this-step p)) - (set-add! flashed-this-step p) - (define flashed-octopi - (for*/fold ([o (hash-set octopi p 0)]) - ([adj (in-list (adjacent-to p))] - #:when (hash-has-key? o adj) - #:unless (set-member? flashed-this-step adj)) - (hash-update o adj add1))) - (values flashed-octopi (add1 flashes)))) - (if (> flashes-this-update 0) - (look-for-more-flashes next-octopus-update (+ flashes-so-far flashes-this-update)) - (values next-octopus-update flashes-so-far)))) - -;; part 1 -(for/fold ([octopi octopus-data] [total-flashes 0] #:result total-flashes) ([step (in-range 100)]) - (define-values [next-state flashes-from-this-state] (simulate-octopi-step octopi)) - (values next-state (+ total-flashes flashes-from-this-state))) - -;; part 2 -(for/fold ([octopi octopus-data] [synchro-step 0] #:result synchro-step) ([step (in-naturals 1)]) - (define-values [next-state flashes-from-this-state] (simulate-octopi-step octopi)) - #:final (= flashes-from-this-state 100) - (values next-state step)) diff --git a/aoc2021/day-12/day-12.rkt b/aoc2021/day-12/day-12.rkt deleted file mode 100644 index 18ed86f..0000000 --- a/aoc2021/day-12/day-12.rkt +++ /dev/null @@ -1,38 +0,0 @@ -#lang racket -(require "../../jj-aoc.rkt" - threading) - -(define path-pairs - (for/list ([l (in-lines (open-day 12 2021))]) - (match (string-split l "-") - [(list start end) (cons start end)]))) - -(define edges-hash (make-hash)) - -(for ([pair (in-list path-pairs)]) - (match-define (cons start end) pair) - (hash-update! edges-hash start (curry cons end) '()) - (hash-update! edges-hash end (curry cons start) '())) - -;; part 1 -(define (backtracking-disallowed? next prevs) - (and (equal? (string-downcase next) next) (member next prevs))) - -(define (look-for-next-cave [path-list '("start")] #:only-one-visit? [visit-used-up? #t]) - (define current-cave (car path-list)) - (cond - [(equal? current-cave "end") (list path-list)] - [else - (~>> (for/list ([next-path (in-list (hash-ref edges-hash current-cave null))] - #:when (and (not (equal? next-path "start")) - (not (and (backtracking-disallowed? next-path path-list) - visit-used-up?)))) - (look-for-next-cave - (cons next-path path-list) - #:only-one-visit? (or (backtracking-disallowed? next-path path-list) visit-used-up?))) - (apply append))])) - -(~> (look-for-next-cave) length time) - -;; part 2 -(~> (look-for-next-cave #:only-one-visit? #f) length time) diff --git a/aoc2021/day-13/day-13.rkt b/aoc2021/day-13/day-13.rkt deleted file mode 100644 index 153eabc..0000000 --- a/aoc2021/day-13/day-13.rkt +++ /dev/null @@ -1,57 +0,0 @@ -#lang racket -(require "../../jj-aoc.rkt" - threading) - -(define (make-pt x y) - (hash 'x x 'y y)) -(struct fold (dir loc) #:transparent) -(define (make-fold dir loc) - (fold (string->symbol dir) (string->number loc))) - -(define data (port->lines (open-day 13 2021))) -(define-values (points-list folds-list) (splitf-at data (λ~> (equal? "") not))) - -(define pts - (for/set ([pt (in-list points-list)]) - (~> pt (string-split ",") (map string->number _) (apply make-pt _)))) - -(define folds - (for/list ([f (in-list (rest folds-list))]) - (~>> f (regexp-match #px"fold along (.)=(.*)") rest (apply make-fold)))) - -(define (fold-over f pts) - (define dir (fold-dir f)) - (define loc (fold-loc f)) - (for/set ([pt (in-set pts)]) - (cond - [(> (hash-ref pt dir) loc) (hash-update pt dir (λ (l) (- (* 2 loc) l)))] - [else pt]))) - -;; part 1 -(~>> pts (fold-over (first folds)) set-count) - -;; part 2 -(define final-pts - (for/fold ([pt pts]) ([f (in-list folds)]) - (fold-over f pt))) - -(define (max-dim pts dim) - (~>> (for/list ([pt (in-set pts)]) - (hash-ref pt dim)) - (apply max))) - -(for ([y (in-inclusive-range 0 (max-dim final-pts 'y))]) - (~>> (for/list ([x (in-inclusive-range 0 (max-dim final-pts 'x))]) - (if (set-member? final-pts (hash 'x x 'y y)) #\█ #\space)) - (apply string) - println)) - -#| -for this data set, the result looks like -" ██ █ █ ██ ██ ███ ██ ██ █ █" -"█ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █" -"█ █ ████ █ █ █ █ █ █ █ █ █" -"████ █ █ █ ██ █ ███ █ ██ ████ █ █" -"█ █ █ █ █ █ █ █ █ █ █ █ █ █ █" -"█ █ █ █ ███ ██ █ ███ █ █ ██ " -|# diff --git a/aoc2021/day-14/day-14.rkt b/aoc2021/day-14/day-14.rkt deleted file mode 100644 index e445694..0000000 --- a/aoc2021/day-14/day-14.rkt +++ /dev/null @@ -1,61 +0,0 @@ -#lang racket -(require "../../jj-aoc.rkt" - threading - memoize - algorithms - list-utils) - -(define data (port->lines (open-day 14 2021))) - -(define (starting-polymer d) - (~> d first string->list (sliding 2 1))) - -(define (first-char d) - (first (first (starting-polymer d)))) - -(define (starting-counts d) - (~> d first frequencies hash->list make-hash)) - -(define (starting-pairs d) - (~>> d (drop _ 2) (map (λ~> (substring 0 2) string->list)))) - -(define (new-pairs d) - (~>> d - (drop _ 2) - (map (λ~> (string-replace " -> " "") - string->list - ((match-lambda - [(list a b c) (list a c b)]) - _) - (sliding 2 1))))) - -(define (transform d) - (~>> (map list (starting-pairs d) (new-pairs d)) (append*) (apply hash))) - -(define transformation (transform data)) - -(define/memo (get-count polymer times) - (match times - [0 - (for/fold ([counts (hash)]) ([pair (in-list polymer)]) - (hash-update counts (second pair) add1 0))] - [_ - (for*/fold ([counts (hash)]) - ([pair (in-list polymer)] - [(c n) (in-hash (get-count (hash-ref transformation pair) (sub1 times)))]) - (hash-update counts c (λ~> (+ n)) 0))])) - -;; part 1 -(define (process-polymer d n) - (~> d - starting-polymer - (get-count _ n) - (hash-update _ (first-char d) add1 0) - hash-values - (sort >) - ((λ (l) (- (first l) (last l)))))) - -(process-polymer data 10) - -;; part 2 -(process-polymer data 40) diff --git a/aoc2021/day-15/day-15-list-nodes.rkt b/aoc2021/day-15/day-15-list-nodes.rkt deleted file mode 100644 index 38c558a..0000000 --- a/aoc2021/day-15/day-15-list-nodes.rkt +++ /dev/null @@ -1,67 +0,0 @@ -#lang racket -(require "../../jj-aoc.rkt" - threading - graph) - -(define data - (for/fold ([cells (hash)]) - ([row (in-lines (open-day 15 2021))] - [x (in-naturals)]) - (for/fold ([cells cells]) - ([n (in-string row)] - [y (in-naturals)]) - (hash-set cells - `(,x ,y) - (~> n string string->number))))) - -(define x-max (~>> data hash-keys (map first) (apply max))) -(define y-max (~>> data hash-keys (map second) (apply max))) - -(define (neighbors pt d) - (match-define (list x y) pt) - (~>> (list (list (add1 x) y) - (list (sub1 x) y) - (list x (add1 y)) - (list x (sub1 y))) - (filter (curry hash-has-key? d)))) - -(define (grid-graph d) - (weighted-graph/directed - (for*/list ([coord (in-list (hash-keys d))] - [neighbor (in-list (neighbors coord d))] - [weight (in-value (hash-ref d neighbor))]) - (list weight coord neighbor)))) - -;; part 1 -(define (find-path-weight d) - (define grid (grid-graph d)) - (let-values ([(node-distances _) (dijkstra grid '(0 0))]) - (define xm (~>> d hash-keys (map first) (apply max))) - (define ym (~>> d hash-keys (map second) (apply max))) - (hash-ref node-distances (list xm ym)))) - -(~> data - find-path-weight - time) - -;; part 2 -(define nine-cycle - (in-cycle (inclusive-range 1 9))) - -(define (expand-data data) - (for/fold ([cells (hash)]) - ([coord (in-list (hash-keys data))]) - (match-define (list x y) coord) - (for*/fold ([cells cells]) - ([n (in-range 5)] - [m (in-range 5)] - [val (in-value (hash-ref data coord))]) - (hash-set cells - (list (+ x (* n (add1 x-max))) - (+ y (* m (add1 y-max)))) - (sequence-ref nine-cycle (+ val n m -1)))))) - -(~> data - expand-data - find-path-weight - time) \ No newline at end of file diff --git a/aoc2021/day-15/day-15.livemd b/aoc2021/day-15/day-15.livemd deleted file mode 100644 index 2495c32..0000000 --- a/aoc2021/day-15/day-15.livemd +++ /dev/null @@ -1,287 +0,0 @@ - - - -# Advent of Code 2021, Day 15 - -## Short summary - -**Parts 1 and 2.** Find the least "risky" path through a grid of nodes, -where each node has a "risk" cost to enter. Part 1's grid is the given data, -while Part 2's is the data after undergoing a transformation. -Only orthogonal travel is possible. - -## Setup - -Using the `libgraph` library to build the graph -and use its implementation of the Dijkstra algorithm: - -```elixir -Mix.install([ - {:libgraph, github: "bitwalker/libgraph"} -]) -``` - -```output -* Getting libgraph (https://github.com/bitwalker/libgraph.git) -remote: Enumerating objects: 783, done. -remote: Counting objects: 100% (64/64), done. -remote: Compressing objects: 100% (52/52), done. -remote: Total 783 (delta 27), reused 24 (delta 10), pack-reused 719 -origin/HEAD set to main -==> libgraph -Compiling 14 files (.ex) -Generated libgraph app -``` - -```output -:ok -``` - -```elixir -floor = - with {:ok, data} <- File.read("./2021/day-15/input") do - data - |> String.split() - |> Enum.map(fn xs -> - String.graphemes(xs) - |> Enum.map(&String.to_integer/1) - end) - end -``` - -```output -[ - [4, 5, 5, 2, 2, 8, 5, 9, 8, 9, 4, 4, 1, 1, 2, 4, 7, 1, 9, 7, 9, 8, 4, 6, 5, 8, 2, 5, 7, 7, 3, 3, - 1, 8, 2, 5, 2, 2, 6, 9, 4, 2, 2, 6, 2, 5, 7, 3, 1, ...], - [8, 3, 1, 1, 8, 2, 6, 9, 1, 7, 6, 7, 7, 2, 9, 5, 5, 6, 1, 3, 9, 5, 7, 1, 6, 2, 4, 5, 4, 2, 6, 1, - 8, 4, 2, 1, 2, 4, 7, 1, 3, 1, 1, 2, 1, 4, 8, 5, ...], - [3, 1, 3, 4, 1, 5, 5, 5, 2, 8, 2, 3, 1, 2, 9, 6, 3, 1, 3, 2, 9, 2, 9, 5, 1, 6, 8, 9, 3, 7, 1, 6, - 7, 1, 6, 1, 1, 3, 5, 5, 1, 9, 9, 4, 2, 9, 3, ...], - [7, 1, 3, 4, 1, 5, 9, 6, 8, 1, 5, 3, 3, 3, 3, 1, 1, 7, 2, 9, 9, 1, 9, 3, 2, 8, 1, 9, 1, 2, 8, 7, - 1, 8, 1, 3, 1, 1, 2, 1, 4, 2, 8, 7, 1, 5, ...], - [3, 2, 9, 1, 6, 2, 2, 1, 2, 1, 8, 9, 3, 8, 2, 1, 2, 5, 8, 2, 1, 5, 1, 1, 1, 5, 3, 1, 6, 1, 4, 2, - 3, 3, 9, 9, 9, 6, 6, 1, 6, 4, 9, 2, 7, ...], - [9, 1, 1, 6, 1, 9, 7, 3, 1, 9, 9, 9, 2, 2, 2, 3, 7, 8, 4, 9, 7, 3, 7, 4, 9, 1, 5, 9, 9, 1, 2, 9, - 1, 8, 9, 4, 8, 5, 1, 4, 8, 5, 5, 2, ...], - [4, 9, 6, 8, 4, 3, 9, 1, 1, 4, 1, 3, 5, 9, 9, 6, 9, 2, 3, 9, 3, 9, 6, 2, 1, 5, 2, 1, 2, 2, 7, 4, - 3, 8, 6, 1, 6, 4, 3, 2, 2, 6, 2, ...], - [6, 2, 3, 9, 2, 1, 9, 7, 8, 5, 2, 3, 4, 6, 7, 5, 3, 9, 1, 2, 8, 1, 6, 4, 1, 1, 1, 4, 1, 4, 8, 2, - 2, 5, 9, 4, 2, 6, 2, 2, 1, 9, ...], - [1, 9, 3, 9, 2, 7, 3, 1, 5, 2, 1, 1, 4, 1, 6, 5, 8, 1, 5, 6, 2, 1, 8, 9, 8, 1, 3, 1, 7, 4, 1, 6, - 2, 5, 1, 4, 2, 2, 7, 3, 7, ...], - [3, 8, 1, 6, 2, 9, 5, 1, 2, 1, 2, 1, 1, 7, 1, 3, 9, 1, 9, 9, 1, 8, 1, 2, 9, 1, 4, 1, 5, 2, 1, 7, - 4, 8, 3, 1, 5, 5, 7, 9, ...], - [2, 8, 5, 2, 1, 7, 4, 3, 9, 2, 1, 7, 5, 2, 1, 7, 8, 9, 1, 2, 9, 2, 7, 6, 9, 2, 9, 7, 3, 2, 9, 7, - 3, 4, 1, 4, 1, 5, 2, ...], - [8, 1, 8, 3, 4, 1, 1, 9, 8, 1, 1, 3, 6, 7, 1, 8, 4, 2, 8, 8, 2, 3, 1, 2, 7, 6, 8, 1, 4, 1, 1, 2, - 4, 6, 5, 2, 8, 2, ...], - [3, 3, 3, 8, 4, 1, 8, 9, 5, 8, 8, 1, 8, 2, 2, 9, 3, 3, 1, 3, 9, 9, 5, 1, 3, 6, 9, 1, 9, 2, 1, 9, - 2, 1, 8, 6, 9, ...], - [6, 1, 1, 8, 5, 8, 2, 7, 6, 5, 3, 7, 5, 5, 4, 1, 1, 4, 4, 4, 9, 1, 5, 1, 9, 8, 7, 1, 1, 2, 3, 1, - 2, 1, 1, 7, ...], - [1, 4, 6, 9, 2, 5, 7, 4, 1, 8, 3, 1, 3, 2, 1, 8, 9, 5, 1, 2, 1, 1, 5, 1, 1, 5, 9, 1, 2, 2, 4, 2, - 8, 1, 8, ...], - [1, 4, 2, 2, 1, 1, 8, 4, 1, 9, 3, 1, 9, 5, 1, 5, 4, 9, 6, 1, 8, 9, 3, 9, 1, 1, 5, 8, 5, 1, 6, 4, - 6, 2, ...], - [8, 1, 6, 2, 5, 4, 6, 1, 3, 9, 9, 5, 2, 1, 3, 6, 1, 8, 9, 9, 1, 1, 1, 7, 7, 5, 3, 3, 3, 5, 7, 8, - 2, ...], - [1, 4, 8, 8, 2, 3, 9, 6, 1, 1, 4, 6, 1, 4, 9, 1, 9, 7, 3, 8, 6, 5, 1, 8, 5, 2, 4, 2, 9, 5, 9, 5, - ...], - [2, 2, 1, 8, 9, 1, 7, 6, 6, 1, 8, 6, 8, 1, 3, 3, 1, 4, 1, 2, 4, 3, 8, 1, 9, 9, 6, 2, 4, 1, 3, ...], - [5, 7, 3, 4, 3, 2, 6, 7, 3, 2, 8, 8, 4, 7, 9, 2, 4, 2, 8, 1, 9, 1, 2, 4, 1, 1, 2, 1, 1, 9, ...], - [1, 5, 1, 1, 9, 3, 5, 1, 1, 1, 8, 6, 1, 1, 2, 1, 9, 3, 5, 7, 4, 1, 7, 6, 8, 3, 3, 8, 9, ...], - [9, 6, 9, 1, 5, 6, 2, 1, 8, 3, 5, 1, 3, 3, 8, 8, 2, 2, 1, 7, 2, 6, 4, 9, 3, 8, 2, 1, ...], - [2, 7, 2, 9, 2, 3, 7, 5, 3, 2, 9, 5, 9, 4, 4, 4, 7, 3, 3, 2, 2, 5, 4, 3, 5, 9, 9, ...], - [7, 3, 1, 2, 5, 5, 9, 1, 8, 1, 3, 2, 3, 7, 4, 3, 9, 3, 8, 2, 2, 1, 2, 1, 2, 2, ...], - [9, 4, 1, 5, 7, 4, 7, 7, 3, 8, 3, 4, 6, 1, 4, 1, 6, 9, 1, 4, 1, 8, 8, 2, 5, ...], - [1, 7, 1, 6, 2, 6, 3, 1, 2, 1, 1, 1, 5, 6, 3, 1, 1, 4, 1, 7, 8, 4, 2, 5, ...], - [2, 1, 1, 3, 4, 1, 4, 9, 1, 1, 7, 3, 1, 1, 6, 4, 9, 6, 2, 3, 4, 2, 2, ...], - [5, 6, 1, 1, 9, 1, 2, 3, 3, 4, 9, 2, 7, 2, 2, 1, 2, 5, 2, 7, 1, 1, ...], - [1, 2, 3, 2, 3, 1, 3, 2, 7, 1, 1, 1, 3, 5, 5, 4, 1, 8, 9, 9, 5, ...], - [2, 9, 2, 8, 1, 2, 3, 1, 2, 1, 1, 9, 1, 2, 6, 7, 7, 2, 1, 3, ...], - [3, 7, 3, 8, 1, 5, 2, 5, 4, 4, 6, 2, 5, 5, 1, 6, 9, 1, 6, ...], - [4, 9, 7, 8, 9, 3, 2, 2, 3, 7, 3, 1, 2, 6, 1, 5, 4, 5, ...], - [3, 9, 1, 2, 8, 9, 9, 1, 8, 2, 7, 9, 8, 9, 1, 6, 6, ...], - [1, 1, 9, 2, 9, 2, 7, 4, 2, 1, 3, 7, 2, 5, 8, 4, ...], - [3, 9, 1, 1, 1, 8, 5, 2, 1, 5, 5, 8, 2, 4, 3, ...], - [2, 9, 5, 3, 2, 1, 6, 7, 2, 2, 8, 1, 5, 2, ...], - [1, 1, 9, 8, 3, 8, 2, 7, 6, 7, 2, 1, 8, ...], - [2, 7, 2, 1, 8, 8, 4, 4, 1, 2, 9, 4, ...], - [3, 1, 1, 5, 1, 1, 8, 2, 2, 6, 2, ...], - [1, 4, 6, 6, 1, 3, 8, 1, 5, 2, ...], - [9, 1, 2, 2, 1, 3, 4, 4, 5, ...], - [6, 6, 1, 9, 4, 1, 3, 2, ...], - [1, 5, 9, 1, 8, 4, 5, ...], - [9, 1, 4, 5, 6, 7, ...], - [1, 7, 5, 6, 3, ...], - [7, 1, 1, 5, ...], - [1, 5, 9, ...], - [1, 5, ...], - [2, ...], - [...], - ... -] -``` - -Give each node a label based on its coordinates: - -```elixir -floor_nodes = - for {row, i} <- Enum.with_index(floor), - {col, j} <- Enum.with_index(row), - into: %{} do - {{i, j}, col} - end -``` - -```output -%{ - {76, 13} => 1, - {37, 47} => 2, - {65, 63} => 5, - {38, 2} => 1, - {1, 26} => 4, - {83, 76} => 2, - {32, 15} => 6, - {89, 14} => 1, - {35, 30} => 7, - {37, 53} => 7, - {4, 5} => 2, - {8, 50} => 7, - {78, 98} => 7, - {95, 56} => 7, - {74, 12} => 9, - {11, 39} => 2, - {65, 43} => 4, - {22, 38} => 1, - {14, 86} => 4, - {20, 41} => 1, - {29, 25} => 1, - {86, 10} => 1, - {83, 36} => 3, - {29, 26} => 3, - {47, 27} => 9, - {4, 81} => 3, - {31, 42} => 1, - {9, 34} => 3, - {90, 0} => 3, - {67, 98} => 1, - {13, 85} => 1, - {63, 81} => 4, - {82, 60} => 4, - {47, 38} => 1, - {15, 92} => 1, - {58, 58} => 1, - {20, 3} => 1, - {61, 95} => 7, - {23, 67} => 4, - {78, 75} => 1, - {79, 17} => 2, - {75, 0} => 7, - {16, 73} => 2, - {76, 2} => 8, - {58, 84} => 1, - {58, 33} => 7, - {47, 44} => 2, - {54, 31} => 6, - {13, ...} => 1, - {...} => 9, - ... -} -``` - -We can travel in the four cardinal directions from each node, so we need -a function to identify a node's neighbors: - -```elixir -neighbors = fn {i, j}, nodes -> - [{i + 1, j}, {i - 1, j}, {i, j + 1}, {i, j - 1}] - |> Enum.filter(&Map.has_key?(nodes, &1)) -end - -[neighbors.({0, 0}, floor_nodes), neighbors.({50, 50}, floor_nodes)] -``` - -```output -[[{1, 0}, {0, 1}], [{51, 50}, {49, 50}, {50, 51}, {50, 49}]] -``` - -## Part 1 - -Now we fold all the edges into a `Graph`: - -```elixir -make_graph = fn nodes -> - for {coord, _} <- nodes, - neighbor <- neighbors.(coord, nodes), - risk = Map.get(nodes, neighbor), - reduce: Graph.new(vertex_identifier: fn v -> v end) do - acc -> Graph.add_edge(acc, coord, neighbor, weight: risk) - end -end - -floor_graph = make_graph.(floor_nodes) -``` - -```output -#Graph -``` - -Now we just need to use Dijkstra's algorithm to find the best path from the upper left to the lower right, -and use the map of each node's risk value to sum up the total risk for the path. - -```elixir -get_lowest_risk_path = fn nodes -> - with {{min_c, _}, {max_c, _}} <- Enum.min_max_by(nodes, fn {{i, j}, _} -> i + j end) do - nodes - |> then(make_graph) - |> Graph.dijkstra(min_c, max_c) - |> tl() - |> Enum.reduce(0, fn coord, sum -> sum + Map.get(nodes, coord) end) - end -end - -get_lowest_risk_path.(floor_nodes) -``` - -```output -403 -``` - -## Part 2 - -The process for Part 2 will be similar; the only difference -is that the graph is five times bigger after the transform. - -If the transformed risk is greater than 9, it rolls over to 1, so a -`Stream.cycle` can be used to easily get the rolled-over values. - -```elixir -expanded_floor_nodes = - with {{max_i, max_j}, _} <- Enum.max_by(floor_nodes, fn {{i, j}, _} -> i + j end), - nine_cycle = Stream.cycle(1..9) do - for {{i, j}, risk} <- floor_nodes, - x <- 0..4, - y <- 0..4, - into: %{} do - {{i + x * (max_i + 1), j + y * (max_j + 1)}, Enum.at(nine_cycle, risk - 1 + x + y)} - end - end - -Enum.count(expanded_floor_nodes) -``` - -```output -250000 -``` - -We repeat the same steps as before: building the graph, -using Dijkstra's algorithm and summing up the risks along the best path. - -```elixir -get_lowest_risk_path.(expanded_floor_nodes) -``` - -```output -2840 -``` diff --git a/aoc2021/day-15/day-15.rkt b/aoc2021/day-15/day-15.rkt deleted file mode 100644 index 6ab67b1..0000000 --- a/aoc2021/day-15/day-15.rkt +++ /dev/null @@ -1,50 +0,0 @@ -#lang racket -(require advent-of-code - threading - graph) - -(struct Point (x y) #:transparent) - -(define data - (for/fold ([cells (hash)]) - ([row (in-lines (open-aoc-input (find-session) 2021 15 #:cache #true))] [x (in-naturals)]) - (for/fold ([cells cells]) ([n (in-string row)] [y (in-naturals)]) - (hash-set cells (Point x y) (~> n string string->number))))) - -(define x-max (~>> data hash-keys (map Point-x) (apply max))) -(define y-max (~>> data hash-keys (map Point-y) (apply max))) - -(define (neighbors pt d) - (match-define (Point x y) pt) - (~>> (list (Point (add1 x) y) (Point (sub1 x) y) (Point x (add1 y)) (Point x (sub1 y))) - (filter (curry hash-has-key? d)))) - -(define (grid-graph d) - (weighted-graph/directed (for*/list ([coord (in-list (hash-keys d))] - [neighbor (in-list (neighbors coord d))] - [weight (in-value (hash-ref d neighbor))]) - (list weight coord neighbor)))) - -;; part 1 -(define (find-path-weight d) - (define grid (grid-graph d)) - (let-values ([(node-distances _) (dijkstra grid (Point 0 0))]) - (define xm (~>> d hash-keys (map Point-x) (apply max))) - (define ym (~>> d hash-keys (map Point-y) (apply max))) - (hash-ref node-distances (Point xm ym)))) - -(~> data find-path-weight time) - -;; part 2 -(define nine-cycle (in-cycle (inclusive-range 1 9))) - -(define (expand-data data) - (for/fold ([cells (hash)]) ([coord (in-list (hash-keys data))]) - (match-define (Point x y) coord) - (for*/fold ([cells cells]) - ([n (in-range 5)] [m (in-range 5)] [val (in-value (hash-ref data coord))]) - (hash-set cells - (Point (+ x (* n (add1 x-max))) (+ y (* m (add1 y-max)))) - (sequence-ref nine-cycle (+ val n m -1)))))) - -(~> data expand-data find-path-weight time) diff --git a/aoc2021/day-16/day-16.rkt b/aoc2021/day-16/day-16.rkt deleted file mode 100644 index 86083ef..0000000 --- a/aoc2021/day-16/day-16.rkt +++ /dev/null @@ -1,97 +0,0 @@ -#lang racket -(require "../../jj-aoc.rkt" - bitsyntax - threading) - -(struct packet (version type type-id contents len) #:transparent) - -(define (BITS->bitstring str) - (integer->bit-string (string->number str 16) (* 4 (string-length str)) #true)) - -(define data (~> (open-day 16 2021) port->string string-trim BITS->bitstring)) - -(define (get-literal-contents bitstr) - (for/fold ([assembled (bit-string)] - [remaining bitstr] - [total-length 6] - [complete? #f] - #:result (values (bit-string->integer assembled #t #f) remaining total-length)) - ([_ (in-naturals)] #:break complete?) - (bit-string-case remaining - ([(= 1 :: bits 1) (number :: bits 4) (remaining :: binary)] - (values (bit-string-append assembled (integer->bit-string number 4 #t)) - remaining - (+ total-length 5) - #f)) - ([(= 0 :: bits 1) (number :: bits 4) (remaining :: binary)] - (values (bit-string-append assembled (integer->bit-string number 4 #t)) - remaining - (+ total-length 5) - #t))))) - -(define (get-type-0-contents cnt bitstr [acc '()] [len 0]) - (cond - [(<= cnt 0) (values (reverse acc) bitstr len)] - [else - (define-values (packet remaining) (identify-next-packet bitstr)) - (get-type-0-contents (- cnt (packet-len packet)) - remaining - (cons packet acc) - (+ len (packet-len packet)))])) - -(define (get-type-1-contents cnt bitstr [acc '()] [len 0]) - (cond - [(= cnt 0) (values (reverse acc) bitstr len)] - [else - (define-values (packet remaining) (identify-next-packet bitstr)) - (get-type-1-contents (sub1 cnt) remaining (cons packet acc) (+ len (packet-len packet)))])) - -(define (identify-next-packet bitstr) - (bit-string-case - bitstr - ([(packet-version :: bits 3) (= 4 :: bits 3) (remaining :: binary)] - (define-values (n now-remaining len) (get-literal-contents remaining)) - (values (packet packet-version 'literal 4 n len) now-remaining)) - ([(packet-version :: bits 3) - (type-id :: bits 3) - (= 0 :: bits 1) - (subpacket-length :: bits 15) - (remaining :: binary)] - (define-values (contents now-remaining sublength) - (get-type-0-contents subpacket-length remaining)) - (values (packet packet-version 'operator type-id contents (+ 22 sublength)) now-remaining)) - ([(packet-version :: bits 3) - (type-id :: bits 3) - (= 1 :: bits 1) - (subpacket-count :: bits 11) - (remaining :: binary)] - (define-values (contents now-remaining sublength) (get-type-1-contents subpacket-count remaining)) - (values (packet packet-version 'operator type-id contents (+ 22 sublength)) now-remaining)))) - -(match-define-values (outer-packet n) (identify-next-packet data)) - -;; part 1 -(define (packet-sum-version p) - (match p - [(packet v 'literal _type-id _contents _len) v] - [(packet v 'operator _type-id ps _len) (foldl (λ (p acc) (+ acc (packet-sum-version p))) v ps)])) - -(packet-sum-version outer-packet) - -;; part 2 -(define packet-f - (match-lambda - [0 +] - [1 *] - [2 min] - [3 max] - [5 (λ (a b) (if (> a b) 1 0))] - [6 (λ (a b) (if (< a b) 1 0))] - [7 (λ (a b) (if (= a b) 1 0))])) - -(define packet-eval - (match-lambda - [(packet _v 'literal _type-id n _len) n] - [(packet _v 'operator type-id ps _len) (~>> ps (map packet-eval) (apply (packet-f type-id)))])) - -(packet-eval outer-packet) diff --git a/aoc2021/day-17/day-17.rkt b/aoc2021/day-17/day-17.rkt deleted file mode 100644 index 7de44a0..0000000 --- a/aoc2021/day-17/day-17.rkt +++ /dev/null @@ -1,43 +0,0 @@ -#lang racket -(require "../../jj-aoc.rkt" - threading) - -(define-values (x-min x-max y-min y-max) - (~> (open-day 17 2021) - port->string - (regexp-match #px"target area: x=(.*)\\.\\.(.*), y=(.*)\\.\\.(.*)\n" _) - rest - (map string->number _) - (apply values _))) - -(define (hit? x y) - (and (x . >= . x-min) (x . <= . x-max) (y . >= . y-min) (y . <= . y-max))) - -(define (miss? x y) - (or (y . < . y-min) (x . > . x-max))) - -(define (drag dx i) - (max (- dx i) 0)) -(define (gravity dy i) - (- dy i)) - -(define (find-trajectory-apex dx dy) - (for/fold ([x 0] [y 0] [y-apex 0] [result #f] #:result (list y-apex result)) - ([i (in-naturals)] #:break result) - (cond - [(hit? x y) (values dx dy y-apex 'hit)] - [(miss? x y) (values x y 'miss 'miss)] - [else (values (+ x (drag dx i)) (+ y (gravity dy i)) (if (y . > . y-apex) y y-apex) #f)]))) - -(define on-target - (for*/list ([dx (in-inclusive-range 1 x-max)] - [dy (in-inclusive-range y-min (abs (* 2 y-max)))] - [velocity (in-value (find-trajectory-apex dx dy))] - #:when (equal? (second velocity) 'hit)) - (list dx dy (first velocity)))) - -;; part 1 -(~>> on-target (argmax third) third) - -;; part 2 -(length on-target) diff --git a/aoc2021/day-18/day-18.rkt b/aoc2021/day-18/day-18.rkt deleted file mode 100644 index 45016b1..0000000 --- a/aoc2021/day-18/day-18.rkt +++ /dev/null @@ -1,5 +0,0 @@ -#lang racket -(require "../../jj-aoc.rkt" - threading) - -;; tbd \ No newline at end of file diff --git a/aoc2021/day-19/day-19.rkt b/aoc2021/day-19/day-19.rkt deleted file mode 100644 index 4c6334d..0000000 --- a/aoc2021/day-19/day-19.rkt +++ /dev/null @@ -1,48 +0,0 @@ -#lang racket -(require "../../jj-aoc.rkt" - threading - racket/struct) - -(struct coord (x y z) #:transparent) - -(define (coord-broadcast f c1 c2) - (match-define (coord x1 y1 z1) c1) - (match-define (coord x2 y2 z2) c2) - (coord (f x1 x2) (f y1 y2) (f z1 z2))) - -(define (coord-reduce f c1 c2) - (foldl (λ (i1 i2 acc) (+ acc (f i1 i2))) 0 (struct->list c1) (struct->list c2))) - -(define coord-delta (curry coord-broadcast -)) -(define coord-sum (curry coord-broadcast +)) -(define coord-manhattan (curry coord-reduce (coord 0 0 0))) - -(define (create-scan-data d) - (for/list ([l (in-list d)]) - (for/list ([pt (in-list (~> (string-split l "\n") rest))]) - (~>> pt - (regexp-match #px"(-?\\d+),(-?\\d+),(-?\\d+)") - rest - (map string->number) - (apply coord))))) - -(define scanner-data (create-scan-data (~>> (open-day 19 2021) port->string (string-split _ "\n\n")))) - -(define (generate-rotations scanner) - (apply - map - list - (for*/list ([pt (in-list scanner)]) - (match-define (coord x y z) pt) - (define orientations (list (list x y z) (list x z (- y)) (list x (- y) (- z)) (list x (- z) y))) - (append* (for/list ([o (in-list orientations)]) - (match-define (list x* y* z*) o) - (list (list x y z) - (list (- x) z y) - (list z (- y) x) - (list y x (- z)) - (list (- y) (- z) x))))))) - -(define (find-overlaps scan1 scan2) - (for/list ([rotation (in-permutations scan2)]) - (map coord-sum scan1 rotation))) diff --git a/aoc2021/day-19/test-scanners b/aoc2021/day-19/test-scanners deleted file mode 100644 index b596cc4..0000000 --- a/aoc2021/day-19/test-scanners +++ /dev/null @@ -1,136 +0,0 @@ ---- scanner 0 --- -404,-588,-901 -528,-643,409 --838,591,734 -390,-675,-793 --537,-823,-458 --485,-357,347 --345,-311,381 --661,-816,-575 --876,649,763 --618,-824,-621 -553,345,-567 -474,580,667 --447,-329,318 --584,868,-557 -544,-627,-890 -564,392,-477 -455,729,728 --892,524,684 --689,845,-530 -423,-701,434 -7,-33,-71 -630,319,-379 -443,580,662 --789,900,-551 -459,-707,401 - ---- scanner 1 --- -686,422,578 -605,423,415 -515,917,-361 --336,658,858 -95,138,22 --476,619,847 --340,-569,-846 -567,-361,727 --460,603,-452 -669,-402,600 -729,430,532 --500,-761,534 --322,571,750 --466,-666,-811 --429,-592,574 --355,545,-477 -703,-491,-529 --328,-685,520 -413,935,-424 --391,539,-444 -586,-435,557 --364,-763,-893 -807,-499,-711 -755,-354,-619 -553,889,-390 - ---- scanner 2 --- -649,640,665 -682,-795,504 --784,533,-524 --644,584,-595 --588,-843,648 --30,6,44 --674,560,763 -500,723,-460 -609,671,-379 --555,-800,653 --675,-892,-343 -697,-426,-610 -578,704,681 -493,664,-388 --671,-858,530 --667,343,800 -571,-461,-707 --138,-166,112 --889,563,-600 -646,-828,498 -640,759,510 --630,509,768 --681,-892,-333 -673,-379,-804 --742,-814,-386 -577,-820,562 - ---- scanner 3 --- --589,542,597 -605,-692,669 --500,565,-823 --660,373,557 --458,-679,-417 --488,449,543 --626,468,-788 -338,-750,-386 -528,-832,-391 -562,-778,733 --938,-730,414 -543,643,-506 --524,371,-870 -407,773,750 --104,29,83 -378,-903,-323 --778,-728,485 -426,699,580 --438,-605,-362 --469,-447,-387 -509,732,623 -647,635,-688 --868,-804,481 -614,-800,639 -595,780,-596 - ---- scanner 4 --- -727,592,562 --293,-554,779 -441,611,-461 --714,465,-776 --743,427,-804 --660,-479,-426 -832,-632,460 -927,-485,-438 -408,393,-506 -466,436,-512 -110,16,151 --258,-428,682 --393,719,612 --211,-452,876 -808,-476,-593 --575,615,604 --485,667,467 --680,325,-822 --627,-443,-432 -872,-547,-609 -833,512,582 -807,604,487 -839,-516,451 -891,-625,532 --652,-548,-490 -30,-46,-14 \ No newline at end of file diff --git a/aoc2021/day-20/day-20.rkt b/aoc2021/day-20/day-20.rkt deleted file mode 100644 index b7ed092..0000000 --- a/aoc2021/day-20/day-20.rkt +++ /dev/null @@ -1,59 +0,0 @@ -#lang racket -(require "../../jj-aoc.rkt" - threading) - -(struct pixel (i j) #:transparent) - -(define-values (raw-enhancement raw-image) - (~> (open-day 20 2021) port->string (string-split "\n\n") (apply values _))) - -(define (char->pixel c) - (if (char=? #\# c) 'light 'dark)) -(define (pixel->char c) - (if (equal? 'light c) #\# #\.)) - -(define enhancement-algorithm - (for/hash ([(c i) (in-indexed (~> raw-enhancement (string-replace "\n" "")))]) - (values i (char->pixel c)))) - -(define image-hash - (for*/hash ([(row i) (in-indexed (string-split raw-image "\n"))] [(c j) (in-indexed row)]) - (values (pixel i j) (char->pixel c)))) - -(define (window->new-pixel p ps bg) - (match-define (pixel i j) p) - (~> (for*/list ([di '(-1 0 1)] [dj '(-1 0 1)]) - (if (equal? 'dark (hash-ref ps (pixel (+ i di) (+ j dj)) bg)) #\0 #\1)) - (apply string _) - (string->number _ 2) - (hash-ref enhancement-algorithm _))) - -(define (pad-hash ps bg) - (define coords (hash-keys ps)) - (define i-min (~>> coords (map pixel-i) (apply min))) - (define i-max (~>> coords (map pixel-i) (apply max))) - (define j-min (~>> coords (map pixel-j) (apply min))) - (define j-max (~>> coords (map pixel-j) (apply max))) - (for*/hash ([i (in-inclusive-range (- i-min 2) (+ i-max 2))] - [j (in-inclusive-range (- j-min 2) (+ j-max 2))]) - (values (pixel i j) (hash-ref ps (pixel i j) bg)))) - -(define (enhance-image ps bg) - (for/hash ([(p _) (in-hash (pad-hash ps bg))]) - (values p (window->new-pixel p ps bg)))) - -;; part 1 -;; looking at the enhancement algorithm, since a window of 0 -> light and 512 -> dark, -;; the infinite background flips colors every other enhancement step -;; instead of trying to account for this algorithmically I just hardcoded it in -(~> image-hash - (enhance-image 'dark) - (enhance-image 'light) - hash-values - (count (curry equal? 'light) _)) - -;; part 2 -(~> (for/fold ([img image-hash]) ([_ (in-range 25)]) - (~> img (enhance-image 'dark) (enhance-image 'light))) - hash-values - (count (curry equal? 'light) _)) diff --git a/aoc2021/day-21/day-21.rkt b/aoc2021/day-21/day-21.rkt deleted file mode 100644 index 9ca9b1b..0000000 --- a/aoc2021/day-21/day-21.rkt +++ /dev/null @@ -1,59 +0,0 @@ -#lang racket -(require threading - memoize) - -;; not going to bother importing the data since it's just two lines of text -(define player-1-start 4) -(define player-2-start 6) - -(define track (sequence->stream (in-cycle (inclusive-range 1 10)))) -(define current-turn (in-cycle (list 'player-1 'player-2))) -(define die-rolls (sequence->stream (in-cycle (inclusive-range 1 100)))) - -;; part 1 -(~> (for/fold ([player-1-score 0] - [player-1-track (stream-tail track (sub1 player-1-start))] - [player-2-score 0] - [player-2-track (stream-tail track (sub1 player-2-start))] - [dice die-rolls] - [last-turn 0] - #:result (list (min player-1-score player-2-score) (* 3 last-turn))) - ([turn-count (in-naturals 1)] - [turn-for current-turn] - #:break (or (player-1-score . >= . 1000) (player-2-score . >= . 1000))) - (define rolls (apply + (stream->list (stream-take dice 3)))) - (match turn-for - ['player-1 - (define next-track (stream-tail player-1-track rolls)) - (values (+ player-1-score (stream-first next-track)) - next-track - player-2-score - player-2-track - (stream-tail dice 3) - turn-count)] - ['player-2 - (define next-track (stream-tail player-2-track rolls)) - (values player-1-score - player-1-track - (+ player-2-score (stream-first next-track)) - next-track - (stream-tail dice 3) - turn-count)])) - (apply * _)) - -;; part 2 -(define d3 (list 1 2 3)) -(define roll-space (~>> (cartesian-product d3 d3 d3) (map (λ~>> (apply +))))) - -(define/memo - (next-turns p1-score p2-score p1-start p2-start) - (cond - [(p1-score . >= . 21) '(1 0)] - [(p2-score . >= . 21) '(0 1)] - [else - (for/fold ([wins '(0 0)]) ([roll (in-list roll-space)]) - (define move-to (add1 (modulo (sub1 (+ roll p1-start)) 10))) - (define possible-wins (next-turns p2-score (+ p1-score move-to) p2-start move-to)) - (list (+ (first wins) (second possible-wins)) (+ (second wins) (first possible-wins))))])) - -(~>> (next-turns 0 0 player-1-start player-2-start) (apply max)) diff --git a/aoc2021/day-22/day-22.rkt b/aoc2021/day-22/day-22.rkt deleted file mode 100644 index 1dc4211..0000000 --- a/aoc2021/day-22/day-22.rkt +++ /dev/null @@ -1,32 +0,0 @@ -#lang racket -(require "../../jj-aoc.rkt" - threading) - -(struct step (instruction xs ys zs) #:transparent) - -;; part 1 -(define (clamped-range nmin nmax) - (in-inclusive-range (max (string->number nmin) -50) - (min (string->number nmax) 50))) - -(define steps - (for/list ([l (in-list (~> (open-day 22 2021) (port->lines)))]) - (~>> l - (regexp-match #px"(.+) x=(-?\\d+)..(-?\\d+),y=(-?\\d+)..(-?\\d+),z=(-?\\d+)..(-?\\d+)") - rest - (match _ [(list direction xmin xmax ymin ymax zmin zmax) - (step (string->symbol direction) - (clamped-range xmin xmax) - (clamped-range ymin ymax) - (clamped-range zmin zmax))])))) - -(~> (for*/fold ([cubes (hash)]) - ([s (in-list steps)] - [to (in-value (step-instruction s))] - [x (step-xs s)] - [y (step-ys s)] - [z (step-zs s)]) - (hash-set cubes (list x y z) to)) - hash-values - (count (curry equal? 'on) _)) - diff --git a/aoc2021/day-25/day-25.rkt b/aoc2021/day-25/day-25.rkt deleted file mode 100644 index 7a3a5ca..0000000 --- a/aoc2021/day-25/day-25.rkt +++ /dev/null @@ -1,35 +0,0 @@ -#lang racket -(require "../../jj-aoc.rkt" - threading) - -(define sea-floor - (for*/hash ([(row i) (in-indexed (in-lines (open-day 25 2021)))] [(c j) (in-indexed row)]) - (values (list i j) c))) - -(define-values (max-i max-j) - (~> sea-floor hash-keys (argmax (λ (coord) (apply + coord)) _) (apply values _))) - -(define (cucumber-movement h c delta-i delta-j) - (define new-hash (hash-copy h)) - (for* ([(coord x) (in-hash h)] #:when (eq? x c)) - (match-define (list i j) coord) - (define neighbor (list (+ delta-i i) (+ delta-j j))) - (define neighbor-or-wrap - (if (hash-has-key? h neighbor) neighbor (list (if (= delta-i 0) i 0) (if (= delta-j 0) j 0)))) - (when (eq? #\. (hash-ref h neighbor-or-wrap)) - (hash-set*! new-hash coord #\. neighbor-or-wrap c))) - new-hash) - -(define (eastwards-movement h) - (cucumber-movement h #\> 0 1)) - -(define (southwards-movement h) - (cucumber-movement h #\v 1 0)) - -;; part 1 -(for/fold ([f sea-floor] [step 0] #:result (add1 step)) ([i (in-naturals 1)]) - (define f* (~> f eastwards-movement southwards-movement)) - #:break (equal? f* f) - (values f* i)) - -;; no part 2 -- merry Christmas diff --git a/aoc2022/commentary.md b/aoc2022/commentary.md deleted file mode 100644 index 8616464..0000000 --- a/aoc2022/commentary.md +++ /dev/null @@ -1,35 +0,0 @@ -# Reflections on Advent of Code 2022 from a guy who kinda knows Racket - -I've gotten into the habit of doing [Advent of Code](https://adventofcode.com/), the annual puzzle-a-day programming challenge. There's a competitive aspect to it, but I'm mostly interested in it as a way to challenge myself and get exposed to algorithms and concepts I haven't seen before. I'm not a programmer by trade; I learned the basics of C in college and a little bit of numerical methods in grad school, but most of what I've learned since then is self-taught as a hobby, so structured programming challenges like this are a good way for me to gauge my skill level and see how CS theory is put into practice. - -The language I'm most comfortable with is [Racket](https://racket-lang.org/), a general-purpose high-level language in the Scheme family, so it's what I used this year for Advent of Code. In my experience, Racket is pretty well-suited for the kinds of problems you find in programming challenges; there hasn't been any problem yet where I've felt like trying to solve it in Racket has been a handicap. For the first few days I used [Jupyter Notebook with the IRacket kernel](https://docs.racket-lang.org/iracket/index.html) to annotate my answers, but I eventually gave up on that because it ended up being harder to debug. - -So, here's my day-by-day opinion on how this year went for me. - -* **[Day 1](https://github.com/hunkyjimpjorps/AdventOfCode/blob/main/aoc2022/day-01/day-01.rkt)**. *Parse a list of lists and return the top three sums.* This is the kind of problem Racket feels made for -- the code is basically a direct declarative translation of the problem statement. -* **[Day 2](https://github.com/hunkyjimpjorps/AdventOfCode/blob/main/aoc2022/day-02/day-02.ipynb)**. *Rock-paper-scissors strategy.* Lots of pattern-matching; the hardest part was probably figuring out the rules of the tournament. -* **[Day 3](https://github.com/hunkyjimpjorps/AdventOfCode/blob/main/aoc2022/day-03/day-03.ipynb)**. *Find common items in rucksacks.* Another problem well-suited to declarative style. -* **[Day 4](https://github.com/hunkyjimpjorps/AdventOfCode/blob/main/aoc2022/day-04/day-04.ipynb)**. *Find overlapping and redundant ranges.* I used the [`rebellion`](https://docs.racket-lang.org/rebellion/index.html) library here for its [range](https://docs.racket-lang.org/rebellion/Ranges.html) data type, which takes care of most of the problem by itself. AoC is a good opportunity to explore a language's ecosystem. -* **[Day 5](https://github.com/hunkyjimpjorps/AdventOfCode/blob/main/aoc2022/day-05/day-05.ipynb)**. *Move stacks of boxes around.* Linked lists make first-in, last-out stack problems like this pretty easy. The major difficulty here was actually parsing the input (and it was probably the hardest input to parse out of all the problems), but once that was resolved it was smooth sailing. -* **[Day 6](https://github.com/hunkyjimpjorps/AdventOfCode/blob/main/aoc2022/day-06/day-06.rkt)**. *Find chunks of unique characters in a string.* Another one that Racket handles without difficulty -- the entire problem statement can be represented as one `for` comprehension. -* **[Day 7](https://github.com/hunkyjimpjorps/AdventOfCode/blob/main/aoc2022/day-07/day-07.rkt)**. *Parse terminal output and calculate directory sizes.* The first speedbump of the year, and probably the first day where there's a rift between "do it right" and "just get the answer". I saw a lot of solutions that properly built the file hierarchy and walked the tree to calculate directory sizes, while I just made a hashtable of absolute paths and the sizes of everything within them. -* **[Day 8](https://github.com/hunkyjimpjorps/AdventOfCode/blob/main/aoc2022/day-08/day-08.rkt)**. *Scout a forest for the treehouse site with the best visibility.* A traditional AoC "search a grid of integers for something" problem, and the first one of the year where I represented a grid as a hashtable of coordinates. Racket doesn't have great multidimensional data structures built in, but rolling my own sparse matrix representation is pretty painless. -* **[Day 9](https://github.com/hunkyjimpjorps/AdventOfCode/blob/main/aoc2022/day-09/day-09.rkt)**. *Simulate a rope.* Simulation problems are my favorite kind of AoC problem (probably because they're the most similar to the kind of stuff I did in school). The solution I wrote for a one-segment rope in part 1 just needed to be folded over a list to give me a solution for the ten-segment rope in part 2. Higher-order functions are nice. -* **[Day 10](https://github.com/hunkyjimpjorps/AdventOfCode/blob/main/aoc2022/day-10/day-10.rkt)**. *The traditional fake-ASM parsing problem*, and another problem that's just largely just folding a function over an accumulator. I feel like if you get comfortable with `for/fold` you can get about 25 AoC stars right off the bat. -* **[Day 11](https://github.com/hunkyjimpjorps/AdventOfCode/blob/main/aoc2022/day-11/day-11.rkt)**. *Monkeys take your stuff.* This problem features my least favorite AoC trope, which is a Part 2 that requires you to know a particular fact about modular arithmetic to solve it in a reasonable amount of time. It also had a data file I found less annoying to retype by hand than to parse. Probably the worst one of the set. -* **[Day 12](https://github.com/hunkyjimpjorps/AdventOfCode/blob/main/aoc2022/day-12/day-12.rkt)**. *Find a hiking route up a spiral mountain.* I leaned pretty hard on the [`graph`](https://docs.racket-lang.org/graph/index.html) package for this one. Eventually I'd like to learn how to implement Dijkstra and A* and other graph traversal algorithms myself, but for now I'm satisfied with just figuring out how to use the prepackaged versions. -* **[Day 13](https://github.com/hunkyjimpjorps/AdventOfCode/blob/main/aoc2022/day-13/day-13.rkt)**. *Sort a recursive data type.* I'm really glad for two features of Racket for this one. First, the ability to read data as code, which meant I could read the nested lists directly with just one tweak to the readtable and without doing any string parsing at all. Second, structural pattern matching. Racket's matching syntax isn't as elegant as some languages (I miss the `[x | xs]` form from Elixir compared to `(list* x xs)`, for example), but it's powerful. -* **[Day 14](https://github.com/hunkyjimpjorps/AdventOfCode/blob/main/aoc2022/day-14/day-14.rkt)**. *Simulate falling sand.* A fun simulation problem, and one that both has a naive solution that solves in a reasonable amount of time and an optimized backtracking solution that's easy to reason out. This was my favorite puzzle of the set. -* **[Day 15](https://github.com/hunkyjimpjorps/AdventOfCode/blob/main/aoc2022/day-15/day-15.rkt)**. *Find the only possible place for a missing beacon.* The fundamental problem here is finding out a way to represent a range of integers sparsely (or [using a library that does that for you](https://docs.racket-lang.org/data/integer-set.html)). Lots of opportunities for optimization. -* **[Day 16](https://github.com/hunkyjimpjorps/AdventOfCode/blob/main/aoc2022/day-16/day-16.rkt)**. *Teach an elephant to open valves*. The first of a few heuristic search problems with enormous solution spaces. Part 1 (one person opening valves) is OK, but I only eventually solved Part 2 (two people simultaneously opening valves) a week later by watching console output and making a reasonable guess. [Nearly half of the remaining participants in AoC quit at this point](https://adventofcode.com/aoc2022/stats) and I don't really blame them. -* **Day 17** (no solution). *Find the period in the pattern of falling rocks.* I'm sure this one isn't too bad, but I was feeling too burnt out by Day 16 to give it more than a token try. -* **[Day 18](https://github.com/hunkyjimpjorps/AdventOfCode/blob/main/aoc2022/day-18/day-18.rkt)**. *Find the surface area of an irregular rock.* A nice straightforward volume-scanning problem, and another opportunity to use my sparse matrix data type. -* **Day 19** (no solution). *Pick the best plan for building mining robots.* Another heuristic search problem, so I skipped this one since I was still failing at day 16 part 2. Picking out an optimized robot building strategy had more moving parts than I knew how to deal with. -* **[Day 20](https://github.com/hunkyjimpjorps/AdventOfCode/blob/main/aoc2022/day-20/day-20.rkt)**. *Repeatedly shift elements around in a circular array.* Singly-linked lists are terrible for repeated arbitrary access, but I don't care. I figured out an algorithm that worked and I was happy enough with that to accept it and move on. After the previous streak of problems, I was just happy to figure out Part 2 on my own. (There was a modulo math trick element to Part 2 here as well, but this one was a lot more obvious to spot.) -* **[Day 21](https://github.com/hunkyjimpjorps/AdventOfCode/blob/main/aoc2022/day-21/day-21.rkt)**. *Figure out what to shout in the monkeys' math game.* I have a feeling the intended solution for Part 2 of this one was to build the expression tree and backtrack through the operations to calculate the unknown value. However, I just blindly started evaluating the result for various guesses and discovered through trial and error that the relationship between the guess and the result is linear, so all you need for part 2 is two guesses, their corresponding results, and some algebra. It's fun to accidentally discover simple solutions to complex-looking problems. -* **Day 22** (no solution). *Something involving walking around on a map.* I skipped this one because of the flu. C'est la vie. -* **[Day 23](https://github.com/hunkyjimpjorps/AdventOfCode/blob/main/aoc2022/day-23/day-23.rkt)**. *Cellular elfomata.* Another AoC staple, though later than usual. I think the story got in the way of the problem description a little bit here, but once I talked through the rules with someone else it was a straight shot to the solution. -* **Day 24** (no solution). *Walk through a blizzard.* I had to skip this one because of holiday travel (ironically made more difficult by a blizzard) and family obligations. It looks interesting, though, so I'm hoping to have some time to come back to it before January. -* **[Day 25](https://github.com/hunkyjimpjorps/AdventOfCode/blob/main/aoc2022/day-25/day-25.rkt)**. *Figure out balanced penternary.* The final AoC problem is always a clever little math brain teaser with one part, and this one was fun to puzzle out with pencil and paper. - -I think Days 16 and 19 could've used a second pass to narrow the possible solution space, or at least make the best heuristic a little more straightforward to reason out, but on the balance, I enjoyed myself this year, and I'm generally happy with my solutions and implementations. \ No newline at end of file diff --git a/aoc2022/day-01/day-01.ipynb b/aoc2022/day-01/day-01.ipynb deleted file mode 100644 index c79a3f6..0000000 --- a/aoc2022/day-01/day-01.ipynb +++ /dev/null @@ -1,139 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Advent of Code 2022\n", - "#### Day 1: Calorie Counting\n", - "\n", - "Elves carry various amounts of food with various caloric contents.\n", - "\n", - "**Part 1.** How many calories is the elf with the most calories of food carrying?\n", - "\n", - "**Part 2.** How many calories are the three elves with the most calories of food carrying?" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "(require racket\n", - " advent-of-code\n", - " threading)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "##### Part 1\n", - "\n", - "The data file is a list of integers, one on each line, with an empty line separating the inventory of each elf.\n", - "\n", - "1. Fetch the input file,\n", - "2. split it on double newlines to find each elf's inventory,\n", - "3. split each inventory on single newlines,\n", - "4. convert each inventory member to a number,\n", - "5. sum up each list, and\n", - "6. find the maximum one. \n", - "\n", - "This is straightforward to do with threading/piping:" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "70374" - ], - "text/plain": [ - "70374" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "(define calorie-data (fetch-aoc-input (find-session) 2022 1))\n", - "\n", - "(~>> calorie-data\n", - " (string-split _ \"\\n\\n\")\n", - " (map (λ~>> string-split (map string->number) (apply +)))\n", - " (apply max))\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "##### Part 2\n", - "\n", - "Similarly, to find the top three calorie holders,\n", - "\n", - "1. fetch the input file,\n", - "2. split it on double newlines,\n", - "3. split each list member on single newlines,\n", - "4. convert each sublist member to a number,\n", - "5. sum up each list, \n", - "6. sort the list from high to low,\n", - "7. take the first three members,\n", - "8. and sum them." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "204610" - ], - "text/plain": [ - "204610" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "(~>> calorie-data\n", - " (string-split _ \"\\n\\n\")\n", - " (map (λ~>> string-split (map string->number) (apply +)))\n", - " (sort _ >)\n", - " (take _ 3)\n", - " (apply +))\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Racket (trusted)", - "language": "racket", - "name": "racket-trusted" - }, - "language_info": { - "codemirror_mode": "scheme", - "file_extension": ".rkt", - "mimetype": "text/x-racket", - "name": "racket", - "pygments_lexer": "racket", - "version": "8.7" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/aoc2022/day-01/day-01.rkt b/aoc2022/day-01/day-01.rkt deleted file mode 100644 index 5215014..0000000 --- a/aoc2022/day-01/day-01.rkt +++ /dev/null @@ -1,20 +0,0 @@ -#lang racket - -(require advent-of-code - threading) - -(define calorie-data (fetch-aoc-input (find-session) 2022 1)) - -;; part 1 -(~>> calorie-data - (string-split _ "\n\n") - (map (λ~>> string-split (map string->number) (apply +))) - (apply max)) - -;; part 2 -(~>> calorie-data - (string-split _ "\n\n") - (map (λ~>> string-split (map string->number) (apply +))) - (sort _ >) - (take _ 3) - (apply +)) diff --git a/aoc2022/day-02/day-02.ipynb b/aoc2022/day-02/day-02.ipynb deleted file mode 100644 index 13b9986..0000000 --- a/aoc2022/day-02/day-02.ipynb +++ /dev/null @@ -1,193 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Advent of Code 2022\n", - "#### Day 2: Rock Paper Scissors\n", - "\n", - "You've given a strategy guide for how to win at a Rock Paper Scissors tournament. The first column is what your opponent will throw. Your score is determined by the result (win, lose, draw) of each round and what you played (rock, paper, scissors).\n", - "\n", - "**Part 1.** What's your tournament score if the second column represents what you should play in each round?\n", - "\n", - "**Part 2.** What's your tournament score if the second column represents the result of each round?\n", - "\n", - "For this solution, I'm using `rackjure` since I'm planning on using a bunch of dictionaries, and `rackjure`'s shorthand makes them easier to work with." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "vscode": { - "languageId": "racket" - } - }, - "outputs": [], - "source": [ - "#lang iracket/lang #:require rackjure\n", - "(require advent-of-code)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The input for this problem is a list with two columns; the first column is one of the characters `A`, `B` or `C` (corresponding to the opponent's throw of rock, paper or scissors) and the second column is `X`, `Y` or `Z`, whose meaning is currently unknown." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "vscode": { - "languageId": "racket" - } - }, - "outputs": [], - "source": [ - "(define strategy-guide (~> (fetch-aoc-input (find-session) 2022 2) (string-split \"\\n\")))\n", - "(define opponent-throw {\"A\" 'rock \"B\" 'paper \"C\" 'scissors})" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We're also given the score for a round result and the bonus for the selected throw, and we write a function that determines the result for a given round." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "vscode": { - "languageId": "racket" - } - }, - "outputs": [], - "source": [ - "(define score-bonus {'rock 1 'paper 2 'scissors 3 'win 6 'draw 3 'lose 0})\n", - "\n", - "(define winning-rounds {'rock 'paper 'paper 'scissors 'scissors 'rock})\n", - "(define losing-rounds {'rock 'scissors 'paper 'rock 'scissors 'paper})\n", - "\n", - "(define (outcome them me)\n", - " (match* (them me)\n", - " [(x x) 'draw]\n", - " [(x y) #:when (eq? y (winning-rounds x)) 'win]\n", - " [(_ _) 'lose]))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "##### Part 1\n", - "\n", - "In part 1, we assume that the second column refers to the throw we should select in each round. We add that to our existing dictionary and write a `for` comprehension to calculate each round result." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "vscode": { - "languageId": "racket" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "13809" - ], - "text/plain": [ - "13809" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "(define assume-throw (dict-merge opponent-throw {\"X\" 'rock \"Y\" 'paper \"Z\" 'scissors}))\n", - "\n", - "(for/sum ([play (in-list strategy-guide)])\n", - " (match-define (list them me) (string-split play))\n", - " (+ (score-bonus (outcome (assume-throw them) (assume-throw me)))\n", - " (score-bonus (assume-throw me))))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "##### Part 2\n", - "Now we're told that the second column actually represents the round result: `X` is lose, `Y` is draw, `Z` is win. We can look up what we should throw in response for each round, and then calculate the score from that." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "vscode": { - "languageId": "racket" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "12316" - ], - "text/plain": [ - "12316" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "(define assume-result (dict-merge opponent-throw {\"X\" 'lose \"Y\" 'draw \"Z\" 'win}))\n", - "(define (pick-throw them result)\n", - " (match* (them result)\n", - " [(x 'draw) x]\n", - " [(x 'win) (winning-rounds x)]\n", - " [(x 'lose) (losing-rounds x)]))\n", - "\n", - "(for/sum ([play (in-list strategy-guide)])\n", - " (match-define (list them result) (string-split play))\n", - " (+ (score-bonus (assume-result result))\n", - " (score-bonus (pick-throw (assume-result them) (assume-result result)))))" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Racket (trusted)", - "language": "racket", - "name": "racket-trusted" - }, - "language_info": { - "codemirror_mode": "scheme", - "file_extension": ".rkt", - "mimetype": "text/x-racket", - "name": "Racket", - "pygments_lexer": "racket", - "version": "8.7" - }, - "orig_nbformat": 4, - "vscode": { - "interpreter": { - "hash": "916dbcbb3f70747c44a77c7bcd40155683ae19c65e1c03b4aa3499c5328201f1" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/aoc2022/day-02/day-02.pl b/aoc2022/day-02/day-02.pl deleted file mode 100644 index 707da41..0000000 --- a/aoc2022/day-02/day-02.pl +++ /dev/null @@ -1,72 +0,0 @@ -:- use_module(library(yall)). -:- use_module(library(apply)). - -output_solutions :- - get_data(Games), - part1_total(Games, Part1), - write(Part1), nl, !, - part2_total(Games, Part2), - write(Part2), nl. - -% Facts - -game(X, X, draw). -game(rock, scissors, win). -game(scissors, paper, win). -game(paper, rock, win). -game(rock, paper, lose). -game(scissors, rock, lose). -game(paper, scissors, lose). - -opponent_move("A", rock). -opponent_move("B", paper). -opponent_move("C", scissors). - -assume_move("X", rock). -assume_move("Y", paper). -assume_move("Z", scissors). - -assume_outcome("X", lose). -assume_outcome("Y", draw). -assume_outcome("Z", win). - -bonus(rock, 1). -bonus(paper, 2). -bonus(scissors, 3). -bonus(lose, 0). -bonus(draw, 3). -bonus(win, 6). - -% Rules - -get_data(Result) :- - setup_call_cleanup(open("2022/day-02/prolog-input.txt", read, In), - (read_string(In, _, Str), - split_string(Str, "\n", "\s\t\n", Lines), - maplist([In, Out] >> split_string(In, "\s", "", Out), Lines, Result)), - close(In)). - -score_game(MyMove, Result, Score) :- - bonus(Result, X), - bonus(MyMove, Y), - Score is X + Y. - -part1_score([Them, Me], Score) :- - opponent_move(Them, TheirMove), - assume_move(Me, MyMove), - game(MyMove, TheirMove, Result), - score_game(MyMove, Result, Score). - -part1_total(Games, Total) :- - maplist(part1_score, Games, Scores), - sum_list(Scores, Total). - -part2_score([Them, Outcome], Score) :- - opponent_move(Them, TheirMove), - assume_outcome(Outcome, Result), - game(MyMove, TheirMove, Result), - score_game(MyMove, Result, Score). - -part2_total(Games, Total) :- - maplist(part2_score, Games, Scores), - sum_list(Scores, Total). \ No newline at end of file diff --git a/aoc2022/day-02/prolog-input.txt b/aoc2022/day-02/prolog-input.txt deleted file mode 100644 index 95a2b2a..0000000 --- a/aoc2022/day-02/prolog-input.txt +++ /dev/null @@ -1,2500 +0,0 @@ -C X -C X -A Y -C X -B Y -A X -A Z -B Y -C Z -C Z -B X -C Z -B Y -C Z -B Y -A Z -B Y -C X -C X -C X -B X -C Z -C X -C Z -C X -A Y -B Y -B Z -A X -C X -C Z -C Z -A Z -B Y -C Z -C X -C X -C Z -B Y -C Z -C Z -C X -B X -B X -A Y -C Z -C Z -B Y -B Y -C Z -C X -A Z -C X -C Z -C Z -B X -C Z -C Z -B Y -A Y -B X -C X -C X -C Z -C X -A Y -C X -C Z -A Z -B Y -C Z -C X -C X -C Z -C Z -C Z -A Z -C Z -A Z -A Z -C X -B Y -C X -C X -A Z -C X -A Y -C Z -C Z -A Y -A Z -C Z -C Z -A Y -C Z -C Y -B Y -B Y -A Z -A Y -C Z -C Z -A Z -C Z -A Y -B Z -C X -C Z -C X -B Y -C Z -C X -B Y -B X -A Y -C Z -C Z -C Z -B Z -A Y -C Z -C Z -C X -C Z -C X -A Y -C Z -C Z -C X -B Y -B X -B Y -C Z -C X -B X -C Z -C Z -A Y -C Z -A Z -A Y -C Z -A Y -C X -A Y -C Y -A Z -C X -C X -B Y -B Y -A Y -A Z -C Z -C X -C Z -A Y -B Y -A Y -B X -C Z -C Z -A Z -A Y -C X -C X -C X -A Z -B Y -C Z -C Z -C Z -C X -B Z -C Z -C Z -B Z -C Z -A Z -B Y -A Y -C Z -B Y -B X -B X -C X -C Z -A X -C Z -C X -C X -C X -C X -C X -B Y -C X -C X -A Y -A Y -C Z -C Z -B X -C Z -C Z -C Z -C Z -A Z -C Z -A Z -B Y -B Y -C Z -B Y -C Z -C Z -C Z -C X -B Y -B Y -C Z -A Z -C Z -C X -B Y -C Z -A Z -C Z -C X -C Z -C Z -C X -C X -B Z -A Y -B X -C Z -B Y -C X -C X -C Z -C Z -B X -B X -B Y -A X -C X -A Z -A Z -C Z -B Y -C Z -A Z -B X -C X -B Y -A Z -C X -A X -A Z -B Y -B X -B Y -A Y -C Z -C Z -B X -C Z -C X -B Y -C Z -C Z -A Y -B Y -A Z -A Y -C X -C X -C Z -A Z -C Z -B Z -A Z -A Y -C X -C Z -C X -B Z -C Z -B Y -A Y -B X -A Y -C Z -A Y -C Z -C X -B Y -C X -A Y -A Z -C Z -B Y -C X -A Y -C X -C Z -C X -B Y -C X -C Z -C Z -A Z -B Y -C X -B X -A Z -C X -C X -A Y -B X -C X -A Z -C Z -C Z -C Z -B Y -A Y -C X -C Z -C Z -A Z -C Z -A Y -C X -C X -C X -A Z -B Y -C Z -A Y -C Z -C Z -C X -C Z -A Z -C Z -B Y -C X -C X -C Z -B Z -B Y -C X -C X -A Y -A Z -A Z -A X -C X -A Y -B Y -A Y -A Z -B Y -B Y -A Y -B Y -C X -A Z -B X -C Z -A Z -B X -A Y -B X -B Y -A Y -A X -C Z -B Z -B X -B Z -C Z -C X -B X -B Y -A Y -B Y -B Y -B Y -A Z -A Y -B X -A Y -C X -B Y -B X -B Y -C X -A Y -C X -A Y -C Z -C Z -A Z -C Z -C X -C X -A Z -C X -C X -C X -A Y -A Z -A Z -C Z -B X -C X -C X -C Z -A Y -C X -C X -B X -C Z -C Z -C X -B Z -C X -C X -C Z -A Y -C X -A Z -C Z -C X -B X -B Y -C X -C Z -C X -C Z -A Z -C Z -C X -C Z -C Z -A Z -B X -C X -C Z -C X -C X -C Z -C X -C Z -A Z -A Z -A Z -C Z -C X -A Z -C Z -C Z -C Z -A Z -B Y -C X -B Y -C X -C Z -B Y -C X -C X -A Z -A Z -C X -C Z -C X -C Z -A Z -A Y -C Z -C Z -A Y -B Y -B Y -C Z -B Y -B X -B Z -A Y -A Z -C X -B Y -B Z -B Y -B Z -C Z -B Y -C Z -C Z -B Y -B X -B Z -C X -A Z -C X -C X -C X -A Z -C Z -A Z -C Z -A Y -C Y -B Y -A Z -B Y -C Z -A Z -A Y -B X -C X -C X -C X -C Z -C X -B X -C Z -A Y -C Z -A X -B Y -B Z -C Z -B Y -C Z -B X -B Y -C Z -B X -A Z -B X -B Y -A Y -B Z -C X -C Z -A Z -A Y -A Z -C X -C Z -B Z -A Z -A Z -C X -C Z -C X -C X -A Z -C X -C X -C X -B X -A Y -B Y -B X -B Y -C Z -B Y -B Z -C Z -C X -B Y -C Z -C Z -C Z -C Z -C Z -C Z -A Z -A Z -A Z -C X -C Z -B Y -C Z -C Z -C Z -C Z -C X -C X -A Z -B Y -A Z -B Z -C X -C X -C Z -C Z -C X -A Y -C X -A Y -C Z -A Z -C Z -B X -C Z -C Z -C X -C X -C Z -B Z -A Y -B X -A Y -B Y -A Y -A Z -A Z -C Z -B X -C Z -C X -C Z -C Z -A Z -C X -A Y -C X -A Z -C Z -C X -C Z -A Z -C X -C X -C X -C X -B X -B Y -C X -C Z -C Z -C Z -C Z -A Z -A Z -A Y -C Z -C X -C Z -C Z -C Z -A Y -C X -A Z -C Y -A Z -C Z -C X -A Y -C Z -C X -C X -A Z -B Y -C Z -A Z -C Z -C Z -C Z -B Y -C X -C X -A X -A Y -C Z -A Z -C Z -B Y -C X -B X -C X -C X -A Y -C Z -C Z -C Z -C X -A Z -B Y -A Y -B Z -B Z -B X -A Z -B X -B X -A Z -A Z -C Y -B Y -C Z -A X -C Z -B X -C Z -A Y -A Y -C Z -C Z -A Z -B Y -C Z -C Z -C Z -C X -A Y -C X -B Y -B Y -C X -C Z -C X -B X -A Y -A Y -C Z -C Z -C Z -C Z -C Z -B X -C Z -A Y -B X -A Y -A Z -C Z -C X -B Y -B Y -C Z -C Z -C Z -B Y -C X -B Y -A Y -B X -C Z -C Z -A Y -C X -C Z -A Y -C X -C X -A Z -C Z -B X -A Z -B Y -C Z -A Z -B X -C Z -B Y -C Z -B Y -C X -C Z -B X -C X -B Y -C X -A Y -C Z -C Z -C X -B Y -C X -C Z -C X -C Z -A Z -A Y -C X -C Z -C Z -A Z -C X -B Z -A Z -B Y -C X -A Z -C Z -B Y -C X -C X -C Z -C Z -B Y -A Z -C Z -C X -C Z -A Z -C X -A Z -C Z -C X -C X -C Z -B X -C Z -C Z -C Z -B Y -A X -B Y -C X -A Z -B X -A Z -C Z -C X -C Z -C Z -B Y -B X -C Z -B Z -B Y -B X -C X -C X -C X -C Z -B Y -C Z -C Z -C Z -C Z -C Z -C Z -C X -C Z -A Y -C X -B X -A Y -C X -C X -C Z -C Z -C X -B X -B Y -B Y -C Z -B X -C X -C X -C Z -C Z -A Y -C Z -A Y -C Z -C Z -B Y -A Z -B X -B X -C Z -C Z -A Y -A Y -C Z -C Z -C X -A Y -A Y -C Z -A Z -C X -B Z -A Y -C X -B Z -A Y -C X -B Y -C X -C X -C Z -A X -C Y -A Y -B Z -B Y -A X -B Y -A Z -C Z -C X -C X -C X -C Y -B Y -C Z -A Z -C X -C X -C Z -C Z -C X -C Z -B Y -C Z -C X -B Y -A Z -C X -B Y -C Z -C X -B Y -A Z -B X -C Z -B X -B X -C Z -C Z -C X -B Y -A Y -B X -B X -A Y -B Y -B Y -B X -A Z -A Y -C Z -B Y -C Z -A Z -C Z -C X -A Z -C X -C Z -C X -B Y -C X -A Z -B X -C Z -C X -C X -B Y -A Y -C Z -C X -A Y -A X -C Z -B Y -B X -C X -C X -C X -C Z -A Z -B Y -A Y -B Y -B X -B Y -B Y -A Z -B Y -B Y -B Y -C Z -C X -A Z -A X -B Z -C X -C X -C X -C X -C Z -A Z -B Y -A Z -B Y -C X -A Z -A Y -C Z -C X -B X -A Z -B Y -C Z -A Z -C X -A Z -A Y -B Y -C Z -B Y -C X -A Z -A Z -A X -C Z -C X -A Y -B Y -B X -C Z -A Z -C X -B X -B Y -A Z -C X -B Y -C X -C Z -C Z -B Y -A Z -A Y -C X -B Y -C X -C X -C Z -C Z -C X -A X -C X -A Z -C Z -B X -B X -C X -B X -B Y -C X -C X -A Y -B Y -C Z -C Z -C Z -C Z -C Z -C Z -A Z -B X -C X -B Y -B Y -C X -C Z -A Z -C X -C Z -C Z -C X -A Z -C Z -A Z -C Z -A Y -C X -C X -B Y -C X -C Z -B X -A Y -C Z -C X -B Y -B X -A Y -C X -A Z -C Z -C X -C X -A Y -B Y -C Z -B Z -C Z -C X -B Y -C Z -C X -A Y -C X -C Z -C X -C X -B X -C X -B Y -C X -C Z -C X -B X -B X -C Z -A Y -C Z -C Y -B X -A Z -C X -A Z -B Z -A Y -C Z -C Z -A Z -B Z -A Z -C X -C Z -C Z -A Z -A Y -C Z -C X -A Y -B Y -B X -A Z -A Y -C X -B X -A Y -C Z -B Y -C X -C X -C X -B X -A Z -B Y -B Y -A Z -C X -B X -B X -A Y -C Z -C X -C X -A Y -C X -C X -C Z -C X -A Y -B Y -C Z -A Z -C Z -A Z -A Z -A Y -C X -C X -C X -A Y -A Y -B Y -B Z -A Z -C X -C Z -C X -C Z -B X -C X -C X -B X -C Z -C X -B Y -B X -C Z -A Z -C Z -B Y -C Z -C Z -B X -A Y -B Y -A Z -B Y -C X -C X -A Z -C Z -C X -C Z -C X -A Z -C X -A Z -C X -A Y -A Z -C X -C Z -B Y -C Z -A Z -C Y -B Z -B Y -A Z -C Z -A X -A Z -C Z -C X -C X -A Y -C Z -C X -C Z -C Z -C Z -B Y -C Z -C X -C Z -B Y -C Z -B Y -C Z -C Z -C Z -C Z -C Z -B Y -C Z -B Y -A Y -C X -B Y -A Y -C X -A Z -A Y -C Z -B Y -C Z -C X -A Y -B Y -C X -C X -C X -A Y -A Z -B X -B X -B X -B Y -C Z -B Y -C Z -C Z -B Y -A X -C X -A Y -C Z -B Y -B Y -C Z -C Z -C X -C Z -C Z -B Y -C Z -C X -A Y -A Z -C X -B Z -C X -B Y -C Z -C X -A Y -A Z -C Z -C X -C Z -C X -C X -C X -C X -C Z -C X -B Y -A Y -C X -C Z -A Z -B Y -C Z -C X -C Z -B Y -A Z -A Y -A Y -B Y -B Y -A Z -B X -A Y -C X -C X -A Y -C X -A Y -B Y -C Z -A X -B X -A Y -A Z -C Z -B X -C Z -B Y -C X -B Y -C Z -A Z -A Y -C X -C Z -B X -B Y -C Z -C Z -A Z -C Z -B Z -C X -C X -C Z -C Z -C Z -C X -C X -C Z -B Y -C Z -B Z -C X -A Y -C Z -B Y -C X -A Y -C Z -B X -C Z -A Z -C Z -C X -A Y -A Y -A Y -C Z -C Z -A Y -A X -C Z -C Z -C X -C Z -A Z -A Z -C Z -C X -C Z -B Y -A Y -B Z -B Y -C X -C Z -C X -B X -B Y -C X -C X -C X -A Z -A Y -C X -C Z -C X -B Y -C Z -B X -C Z -B Y -A Z -C X -B Y -C X -B X -A Z -C X -B Y -A Y -C Z -C X -A Y -C X -B Y -B X -C Z -C X -C X -C Z -C X -C X -A Y -A X -C Z -C Z -C Z -B Y -C Z -A Z -B X -C Z -C X -C X -A Y -A Z -B Y -B Y -C Z -C Z -A Y -A Z -A Z -C Z -B Y -B Y -C Z -B Y -C X -C X -C Z -A Z -C X -C X -B Y -B Y -C Z -B Y -C Z -C Z -C Z -C Z -C X -C Z -B Y -C Z -C Z -B X -C Z -C Z -C X -B Y -C Z -A Z -A Z -A Z -C X -B Y -C Z -A Y -B X -C X -C X -A X -A Y -B X -C Z -C X -C Z -C Z -B Y -C Z -C Z -B Z -C Z -C Z -B X -A Y -C X -A Z -B Y -A Z -C X -B X -B X -C Z -A X -B Z -A Z -B Y -C X -C Z -B Z -C X -B X -C Z -A Z -B X -C Z -C Z -A Z -A Y -C Z -C Z -B X -A Z -C Z -B Y -B Y -C Z -B Y -C Z -C Z -C Z -C Z -C Z -C Z -C Z -C X -A Y -C Z -C Z -C X -C Z -B Y -B X -B Y -A Z -C Z -B Y -B X -C Z -B X -C Y -C X -B Y -C Z -B Y -A Z -B Y -C X -C Z -B X -C Z -A Y -C Z -B X -A Z -A Y -B Y -C Z -A Y -B Y -C X -A Z -A Y -C Z -C Z -A Y -B X -C X -B Y -A Y -C X -B X -C X -C Z -C Z -B Y -A Z -B Y -A Z -A Y -A Z -B X -A Y -C X -B Y -C Z -C Z -A Z -C Z -C Z -C X -C Z -C Z -B Y -A Z -C X -A Y -C Z -A X -A Z -C Z -B Y -C X -C Y -A Y -B X -B Y -C Z -C Z -B X -B X -C Y -B Y -B X -C X -C Z -A Z -C Z -A Z -C Z -C Z -A Z -B X -C Z -C X -C Z -B Y -B X -A Z -C Z -B X -C Z -B Z -C Z -B Y -B Y -C Z -B Y -A Y -A Z -A Z -C X -A X -C X -C Z -C Z -A Y -C Z -C Z -C Z -A Y -B X -C Z -C X -B X -C X -C X -B Y -C Z -B X -C X -B Y -A Y -C Z -C X -C X -A Z -B Y -C Z -B Y -C Z -B Z -B Y -B Y -A Y -B Y -B Y -A X -A Y -C Z -C X -B X -C Z -C X -C X -B Y -C Z -C Z -B Y -A Y -B Y -C Z -C X -C Z -C Z -C X -A Y -A Y -C Z -A Y -C X -C Z -A Z -A Z -C Z -B Y -A X -A X -B X -A Z -C X -C X -C X -C X -A Z -A Z -C X -B X -B Y -C X -B Y -B Z -A Z -A Y -C X -B Y -B Y -C Z -B X -B X -C Z -B Y -C Z -C Z -B Y -C Z -C X -C Z -B Y -C X -C Z -C Z -C X -B X -C Z -C X -C X -C Y -C Z -B Y -C X -C X -A Y -C X -C X -C Z -C X -C Z -C Z -C Z -A X -C Z -C Y -C Z -C Z -C Z -C Z -A X -C Z -B X -C Z -C X -B Y -C X -A Z -C Z -C X -B Y -B Y -A Z -A Y -C X -A Z -C Z -C Z -A Y -A Z -A X -C Z -A Z -C Z -C Z -C Z -A Z -A Y -A Z -A Z -A Y -A Y -C X -A Y -A Y -C Z -C Z -C Z -B X -C X -C Z -B X -C Z -C Z -C Z -C Z -C Z -B Y -C Z -B Y -C Z -C X -A Z -C Z -B X -B Y -C X -B Y -C X -C X -A Y -C Z -C X -C X -C Z -C X -C X -C Z -B Y -B X -C X -B X -B Y -C X -A Z -B Y -A Z -B Y -A Z -A Y -C Z -C Z -C X -B Y -A Y -A Y -C X -B Y -C X -A Z -A Z -C X -A X -C X -A Z -C X -C Z -C X -A Y -C X -B Y -B Y -C X -A Z -C Z -C Z -B X -C Z -C Z -A Y -C Z -B Y -A Z -A X -C Z -C X -B Y -A Z -C X -B X -A Z -A Z -C Z -A Z -C Z -C Z -A X -C X -A Z -C X -A Y -B Y -C Z -B Y -B X -C Z -A X -B Z -A Z -A Z -C Z -A Z -C Z -A Z -C X -C X -C X -C X -A Z -C Z -C X -B Y -A Z -B Y -A Y -C Z -A Z -C X -C Z -B Y -C X -A Y -B X -A Y -B X -B Y -C Z -A Y -B Y -A Y -B Y -C Z -C X -C X -C X -C Z -B Y -C Z -B X -C X -A Y -A X -B Y -A Z -C Z -A Y -A Z -C X -B Y -A Z -C Z -C X -C Y -C X -A Z -A Y -C X -A Y -C X -C X -A Z -B Y -C Z -C X -B X -B Y -A Z -A Z -B X -B Y -C X -B Y -C Z -B Y -C Y -B Y -B X -B Y -A Y -B Y -C Z -B X -B Y -C Z -A Z -C Z -B X -A Y -B X -C Z -B Y -A Y -C Z -C Z -C Y -C Z -A Y -C X -A Y -C Z -C Z -C Z -C Z -C Z -C X -C Z -C Z -B Y -C X -C Z -B X -C X -C Z -C X -A Z -C X -C X -C Z -C X -C X -C Z -A Z -B X -B Y -C Z -A Z -C X -B X -B Y -C Z -C Z -A Y -C X -C Z -C X -C Z -B Y -C X -C Z -C X -C X -C Z -C X -B X -C X -C Z -A Y -A Y -A Y -C Z -C Z -B X -C X -A Z -C Z -C X -C Z -B X -C Z -B Z -C X -A Y -C Z -A Y -C X -B Y -B Y -A Z -C Z -C Z -C Z -A Z -C X -A Y -C Z -A Y -C Z -A Z -A Z -B Y -C Z -A Z -C X -A Y -C Z -C Z -C X -C X -C X -A X -B Z -C Z -C X -A Y -A Z -C Z -B X -B Y -C X -C Z -B X -B Y -C X -B X -C Z -C X -C X -C X -C X -A Y -A Z -A Y -B Y -C Z -B Y -B Y -C Z -A X -C Z -B X -C Z -C Z -A Z -B Y -C Z -C Z -A Y -A Z -A Z -A Z -B X -C X -C X -B Y -C Z -C X -B Y -A Y -C X -C Z -C Z -C X -C Y -A Y -A Z -C X -C X -A Z -B X -C Z -A X -C Z -C X -C Z -A X -A Z -C X -B Y -C Z -C Z -B X -A Y -B Y -C Z -C Z -A Z -A Y -C Z -C Z -A X -B Y -C Z -C Z -B Y -C X -C Z -B X -B Y -A Z -C Z -C X -C X -C Z -B Z -B Y -C Z -C X -A Y -C X -A Y -A Z -B Y -B X -C Z -A Z -C Z -C Z -C Z -A Y -B Y -A Z -B Y -C Z -C X -B Y -C Z -A Y -C Z -A Z -C Z -C Z -C Z -A Z -C X -B Y -C Z -A Z -C Z -C Z -A Z -C Z -C X -B Z -C Z -C Z -A Z -C Z -C Z -B Y -B X -C Z -C Z -B Y -A Y -C Z -A Z -A Z -C Z -C X -C X -B X -C X -A Y -B X -A Y -C Z -C X -B Y -C Z -C X -C X -C X -B X -B Z -C Z -B Y -C X -A Z -C Z -C Z -A Z -A Z -C Z -C Z -A Z -A Z -A Z -C Z -A Z -C Y -A Z -C Z -C Z -C X -C X -C Z -C Z -A Z -C Z -A Z -C X -C X -C X -A Y -A Z -A Z -A Y -B X -C Z -C Z -B X -C Z -C X -B Y -C Z -C X -C X -B Y -B Y -A Y -A Z -B X -C X -B Y -B Y -B Y -C Z -C X -C X -A Y -B Z -A Z -A Z -B X -C X -A Y -C X -C Z -C X -A Y -A Z -C Z -A Z -C Z -C X -C X -A Z -B X -B X -B Y -C Z -C Z -C X -C X -C X -B Y -C Z -C X -B X -C Z -C X -B X -A Y -B Y -C Z -A Z -A Y -C Z -A Y -A Y -C Z -B Y -C Z -B Z -C Z -A Z -C X -C X -C Z -B X -C X -B Y -C Z -B X -C Z -C X -B X -B Y -B Z -B X -A Y -C X -C Z -C Z -A Y -B X -A Y -C X -C Z -B Y -C Z -C Z -C Z -A Z -A Z -A Y -C Z -B Y -C Z -C X -B Y -C Z -C Z -A X -C X -B Y -A Z -C Z -C X -A Z -A Y -C Z -C Z -B X -C Z -A Z -A Y -C X -A Y -C Z -C X -C X -C X -B Y -C X -A Y -C X -B Y -B X -A Y -B X -A Y -B Z -C Z -A Z -B Y -B X -C Z -C Z -C X -B Y -A Z -A Z -A Y -A Y -C Z -A Z -A Z -B Y -C X -C Z -C Z -C Z -A Y -A Z diff --git a/aoc2022/day-03/day-03.ipynb b/aoc2022/day-03/day-03.ipynb deleted file mode 100644 index 27b8086..0000000 --- a/aoc2022/day-03/day-03.ipynb +++ /dev/null @@ -1,168 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Advent of Code 2022\n", - "#### Day 3: Rucksack Reorganization\n", - "\n", - "**Part 1.** Every elf has a rucksack with two compartments. What's the total priority value of the items that appear in both compartments of each sack?\n", - "\n", - "**Part 2.** Each group of three elves is carrying exactly one item in common. What's the total priority value of those common items?\n", - "\n", - "To ease the conversion between strings, lists and sets I bring in some utility functions from `relation`. " - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "vscode": { - "languageId": "racket" - } - }, - "outputs": [], - "source": [ - "(require racket\n", - " advent-of-code\n", - " threading\n", - " (only-in relation ->list ->set ->char))\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The items in each elf's inventory are represented by a string of alphabetic (case-sensitive) characters, each with a \"priority value\":\n", - "* Lowercase item types `a` through `z` have priorities 1 through 26.\n", - "* Uppercase item types `A` through `Z` have priorities 27 through 52." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "vscode": { - "languageId": "racket" - } - }, - "outputs": [], - "source": [ - "(define priority\n", - " (for/hash ([i (in-naturals 1)]\n", - " [c (in-sequences (inclusive-range 97 122) (inclusive-range 65 90))])\n", - " (values (->char c) i)))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "##### Part 1\n", - "\n", - "The front half and back half of the rucksack have one item in common we need to identify. For each rucksack in the inventory, we apply the following steps:\n", - "\n", - "1. Split the bag into the two halves,\n", - "2. find the common item in both halves (the intersection of the sets),\n", - "3. then look up that common item's value. " - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "vscode": { - "languageId": "racket" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "7845" - ], - "text/plain": [ - "7845" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "(define raw-inventory (~> (fetch-aoc-input (find-session) 2022 3) string-split))\n", - "\n", - "(for/sum ([bag (in-list raw-inventory)])\n", - " (define len (/ (string-length bag) 2))\n", - " (~>> bag\n", - " ->list\n", - " ((λ (xs) (list (take xs len) (drop xs len)))) ; step 1\n", - " (apply set-intersect _) ; step 2\n", - " set-first\n", - " (hash-ref priority))) ; step 3\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "##### Part 2\n", - "\n", - "Now we need to take three rucksacks at a time and find the common item they all share. The procedure is simpler than Part 1's, especially with the `in-slice` function that automatically forms groups of three." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "vscode": { - "languageId": "racket" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "2790" - ], - "text/plain": [ - "2790" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "(for/sum ([bags (in-slice 3 raw-inventory)])\n", - " (~>> bags (map ->set) (apply set-intersect) set-first (hash-ref priority)))\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Racket (Trusted)", - "language": "racket", - "name": "racket-trusted" - }, - "language_info": { - "codemirror_mode": "scheme", - "file_extension": ".rkt", - "mimetype": "text/x-racket", - "name": "Racket", - "pygments_lexer": "racket", - "version": "8.7" - }, - "orig_nbformat": 4, - "vscode": { - "interpreter": { - "hash": "7454d72389401259f8ab87ac90deac92d19baedf4a52e60301852b1f4c653c5c" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/aoc2022/day-04/day-04.ipynb b/aoc2022/day-04/day-04.ipynb deleted file mode 100644 index 44c8980..0000000 --- a/aoc2022/day-04/day-04.ipynb +++ /dev/null @@ -1,148 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Advent of Code 2022\n", - "#### Day 4: Camp Cleanup\n", - "\n", - "Each elf in a pair of elves is assigned a range of ID numbers.\n", - "\n", - "**Part 1.** How many pairs have one elf assigned to a range completely contained by the other's?\n", - "\n", - "**Part 2.** How many pairs have overlapping assignments?\n", - "\n", - "Since this problem heavily uses ranges, I'm using `rebellion/base/range` to do the heavy lifting." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "vscode": { - "languageId": "racket" - } - }, - "outputs": [], - "source": [ - "#lang iracket/lang #:require rackjure\n", - "(require advent-of-code\n", - " relation\n", - " rebellion/base/range)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "##### Part 1\n", - "\n", - "All the assignments look like `\"11-22,33-44\"`, so we write a function to extract the numbers from the string with a regex, decompose the values with structural matching, and construct a pair of [`rebellion` ranges](https://docs.racket-lang.org/rebellion/Ranges.html).\n", - "\n", - "Once we have the two ranges, we can use a predicate that tests if one completely contains the other or vice versa to count the corresponding assignments." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "vscode": { - "languageId": "racket" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "550" - ], - "text/plain": [ - "550" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "(define assignments (~> (fetch-aoc-input (find-session) 2022 4) string-split))\n", - "\n", - "(define (parse-range str)\n", - " (match str\n", - " [(regexp #px\"(\\\\d+)-(\\\\d+),(\\\\d+)-(\\\\d+)\" (list _ a b c d))\n", - " (values (closed-range (->number a) (->number b)) (closed-range (->number c) (->number d)))]))\n", - "\n", - "(define (one-encloses-the-other? str)\n", - " (define-values (range1 range2) (parse-range str))\n", - " (or (range-encloses? range1 range2) (range-encloses? range2 range1)))\n", - "\n", - "(count one-encloses-the-other? assignments)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "##### Part 2\n", - "\n", - "The procedure for part 2 is the same, just using the predicate for overlapping ranges instead." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "vscode": { - "languageId": "racket" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "931" - ], - "text/plain": [ - "931" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "(define (one-overlaps-the-other? str)\n", - " (define-values (range1 range2) (parse-range str))\n", - " (range-overlaps? range1 range2))\n", - "\n", - "(count one-overlaps-the-other? assignments)\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Racket (Trusted)", - "language": "racket", - "name": "racket-trusted" - }, - "language_info": { - "codemirror_mode": "scheme", - "file_extension": ".rkt", - "mimetype": "text/x-racket", - "name": "Racket", - "pygments_lexer": "racket", - "version": "8.7" - }, - "orig_nbformat": 4, - "vscode": { - "interpreter": { - "hash": "916dbcbb3f70747c44a77c7bcd40155683ae19c65e1c03b4aa3499c5328201f1" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/aoc2022/day-05/day-05.ipynb b/aoc2022/day-05/day-05.ipynb deleted file mode 100644 index 34cf4e4..0000000 --- a/aoc2022/day-05/day-05.ipynb +++ /dev/null @@ -1,187 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Advent of Code 2022\n", - "#### Day 5: Supply Stacks\n", - "\n", - "You're operating a crane, following a set of instructions telling you how many boxes to move from one stack to another. After you follow the instructions, the top boxes in the stacks spell out a message.\n", - "\n", - "**Part 1.** What's the message if the crane moves one box at a time?\n", - "\n", - "**Part 2.** What's the message if the crane moves all of the boxes at once?\n", - "\n", - "I'm bringing in my usual utility functions." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "(require racket\n", - " advent-of-code\n", - " threading\n", - " (only-in relation ->string ->list ->number)\n", - " (only-in algorithms chunks-of))\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The data file in this one is unusual: the first 10 lines are a pictorial representation of the stacks of boxes, and the remaining lines are instructions in the form `move X from Y to Z`.\n", - "\n", - "Tackling the boxes first, I can get the contents of each stack if I treat the picture as an array and transpose it, drop the first row that contains only brackets, then only take rows 1, 5, 9..., which are the rows with letters in them and trim the leading spaces from each row. I then use this list to create a hashtable." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "(define assignments (~> (fetch-aoc-input (find-session) 2022 5) (string-split \"\\n\")))\n", - "\n", - "(define crates-list\n", - " (~>> assignments\n", - " (take _ 8)\n", - " (map ->list)\n", - " (apply map list _)\n", - " rest\n", - " (chunks-of _ 4)\n", - " (map (λ~> first ->string string-trim ->list) _)))\n", - "\n", - "(define crates\n", - " (for/hash ([c (in-list crates-list)] [i (in-naturals 1)])\n", - " (values i c)))\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The instructions are a little easier; this is just a regex and destructuring like in Day 4." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "(struct instruction (n from to))\n", - "\n", - "(define (parse-instruction str)\n", - " (match str\n", - " [(regexp #px\"move (\\\\d+) from (\\\\d) to (\\\\d)\" (list _ n from to))\n", - " (instruction (->number n) (->number from) (->number to))]))\n", - "\n", - "(define instructions (~>> assignments (drop _ 10) (map parse-instruction)))\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "##### Part 1\n", - "\n", - "The main function to iterate over the list of instructions is the same for both parts, except for whether the boxes taken off of the origin stack are reversed or not when they end up on the destination stack. They end up reversed if they're taken off one at a time, and don't reverse if the whole stack is picked up at once.\n", - "\n", - "Once I've iterated through all the instructions, the `#:result` clause parses the final crate state." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\"WHTLRMZRC\"" - ], - "text/plain": [ - "\"WHTLRMZRC\"" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "(define (find-crate-message cs [reverse? #true])\n", - " (define direction (if reverse? reverse identity))\n", - " (for/fold ([current-crates cs]\n", - " #:result (~>> (hash-values current-crates) (map first) (apply string)))\n", - " ([i (in-list instructions)])\n", - " (match-define (instruction n from to) i)\n", - " (define taken (~> (hash-ref current-crates from) (take _ n) direction))\n", - " (~> current-crates\n", - " (hash-update _ from (λ (v) (drop v n)))\n", - " (hash-update _ to (λ (v) (append taken v))))))\n", - "\n", - "(find-crate-message crates)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "##### Part 2\n", - "\n", - "The result, if the moved boxes don't get flipped:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\"GMPMLWNMG\"" - ], - "text/plain": [ - "\"GMPMLWNMG\"" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "(find-crate-message crates #false)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Racket (Trusted)", - "language": "racket", - "name": "racket-trusted" - }, - "language_info": { - "codemirror_mode": "scheme", - "file_extension": ".rkt", - "mimetype": "text/x-racket", - "name": "racket", - "pygments_lexer": "racket", - "version": "8.7" - }, - "orig_nbformat": 4, - "vscode": { - "interpreter": { - "hash": "916dbcbb3f70747c44a77c7bcd40155683ae19c65e1c03b4aa3499c5328201f1" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/aoc2022/day-05/day-05.rkt b/aoc2022/day-05/day-05.rkt deleted file mode 100644 index 76d4ca6..0000000 --- a/aoc2022/day-05/day-05.rkt +++ /dev/null @@ -1,46 +0,0 @@ -#lang racket - -(require advent-of-code - threading - (only-in relation ->string ->list ->number) - (only-in algorithms chunks-of)) - -(define data (~> (fetch-aoc-input (find-session) 2022 5) (string-split "\n"))) - -(struct instruction (n from to)) - -(define crates-list - (~>> data - (take _ 8) - (map (λ~>> ->list)) - (apply map list _) - rest - (chunks-of _ 4) - (map (λ~> first ->string string-trim ->list) _))) - -(define crates - (for/hash ([c (in-list crates-list)] [i (in-naturals 1)]) - (values i c))) - -(define (parse-instruction str) - (match str - [(regexp #px"move (\\d+) from (\\d) to (\\d)" (list _ n from to)) - (instruction (->number n) (->number from) (->number to))])) - -(define instructions (~>> data (drop _ 10) (map parse-instruction))) - -(define (find-crate-message cs [reverse-function reverse]) - (for/fold ([current-crates cs] - #:result (~>> (hash-values current-crates) (map first) (apply string))) - ([i (in-list instructions)]) - (match-define (instruction n from to) i) - (define taken (~> (hash-ref current-crates from) (take _ n) reverse-function)) - (~> current-crates - (hash-update _ from (λ (v) (drop v n))) - (hash-update _ to (λ (v) (append taken v)))))) - -;; part 1 -(find-crate-message crates) - -;; part 2 -(find-crate-message crates identity) diff --git a/aoc2022/day-06/day-06.ipynb b/aoc2022/day-06/day-06.ipynb deleted file mode 100644 index 0c89fa1..0000000 --- a/aoc2022/day-06/day-06.ipynb +++ /dev/null @@ -1,122 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Advent of Code 2022\n", - "#### Day 6: Tuning Trouble\n", - "\n", - "You're parsing a buffer of characters, looking for a \"marker\": the index of the first character where the previous $n$ characters are all unique.\n", - "\n", - "**Part 1.** What if $n = 4$, for the \"start of packet\" marker?\n", - "\n", - "**Part 2.** What if $n = 14$, for the \"start of message\" marker?" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "(require racket\n", - " advent-of-code\n", - " (only-in algorithms sliding))\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "##### Parts 1 and 2\n", - "\n", - "`(sliding xs n)` returns all the overlapping sublists of `xs` that are `n` elements wide. This turns this problem into a simple `for` comprehension, just looking for the first sublist with no duplicates (which means its length after deduplication doesn't change).\n", - "\n", - "The solution to part 1 and part 2 is the same, just differing in how wide the window is." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "1042" - ], - "text/plain": [ - "1042" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "(define buffer (fetch-aoc-input (find-session) 2022 6))\n", - "\n", - "(define (find-marker data type)\n", - " (define n\n", - " (match type\n", - " ['start-of-packet 4]\n", - " ['start-of-message 14]))\n", - " (for/first ([chunk (in-list (sliding (string->list data) n))]\n", - " [i (in-naturals n)]\n", - " #:when (= n (~> chunk remove-duplicates length)))\n", - " i))\n", - "\n", - "(find-marker buffer 'start-of-packet)\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "2980" - ], - "text/plain": [ - "2980" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "(find-marker buffer 'start-of-message)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Racket (trusted)", - "language": "racket", - "name": "racket-trusted" - }, - "language_info": { - "codemirror_mode": "scheme", - "file_extension": ".rkt", - "mimetype": "text/x-racket", - "name": "racket", - "pygments_lexer": "racket", - "version": "8.7" - }, - "orig_nbformat": 4, - "vscode": { - "interpreter": { - "hash": "916dbcbb3f70747c44a77c7bcd40155683ae19c65e1c03b4aa3499c5328201f1" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/aoc2022/day-06/day-06.rkt b/aoc2022/day-06/day-06.rkt deleted file mode 100644 index 1c167a6..0000000 --- a/aoc2022/day-06/day-06.rkt +++ /dev/null @@ -1,22 +0,0 @@ -#lang racket - -(require advent-of-code - (only-in algorithms sliding)) - -(define buffer (fetch-aoc-input (find-session) 2022 6)) - -(define (find-marker data type) - (define n - (case type - [(start-of-packet) 4] - [(start-of-message) 14])) - (for/first ([chunk (in-list (sliding (string->list data) n))] - [i (in-naturals n)] - #:unless (check-duplicates chunk)) - i)) - -;; part 1 -(find-marker buffer 'start-of-packet) - -;; part 2 -(find-marker buffer 'start-of-message) diff --git a/aoc2022/day-07/day-07.rkt b/aoc2022/day-07/day-07.rkt deleted file mode 100644 index 3826cc4..0000000 --- a/aoc2022/day-07/day-07.rkt +++ /dev/null @@ -1,36 +0,0 @@ -#lang racket - -(require advent-of-code - fancy-app - threading - (only-in relation ->number)) - -(define command-output (~> (fetch-aoc-input (find-session) 2022 7) (string-split "\n"))) - -(define (parse-commands cmds) - (define (update-sizes h path size) - (match path - ['() h] - [(list* _ fs) (update-sizes (hash-update h path (+ _ size) 0) fs size)])) - - (for/fold ([folders (hash)] [current-path '()] [previously-seen? #false] #:result folders) - ([cmd (in-list cmds)]) - (match (string-split cmd) - [(list "$" "ls") (values folders current-path (hash-has-key? folders current-path))] - [(list "$" "cd" "/") (values folders '("/") #false)] - [(list "$" "cd" "..") (values folders (rest current-path) #false)] - [(list "$" "cd" folder) (values folders (cons folder current-path) #false)] - [(list "dir" _) (values folders current-path previously-seen?)] - [(list (app ->number size) _) - (cond - [previously-seen? (values folders current-path previously-seen?)] - [else (values (update-sizes folders current-path size) current-path previously-seen?)])]))) - -(define folders (parse-commands command-output)) - -;; part 1 -(for/sum ([(_ v) (in-hash folders)] #:when (<= v 100000)) v) - -;; part 2 -(define required-to-delete (- 30000000 (- 70000000 (hash-ref folders '("/"))))) -(~>> folders hash-values (filter (> _ required-to-delete)) (apply min)) diff --git a/aoc2022/day-08/day-08.ipynb b/aoc2022/day-08/day-08.ipynb deleted file mode 100644 index 890a9bb..0000000 --- a/aoc2022/day-08/day-08.ipynb +++ /dev/null @@ -1,257 +0,0 @@ -{ - "cells": [ - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Advent of Code 2022\n", - "#### Day 8: Treetop Tree House\n", - "\n", - "For a rectangular grid of trees of varying heights,\n", - "\n", - "**Part 1.** How many trees are visible (not blocked by same-height or taller trees in their row or column) from outside the patch?\n", - "\n", - "**Part 2.** What's the tree with the best \"scenic score\" (the product of the number of trees it can see in each cardinal direction)?\n", - "\n", - "For this solution, I didn't use any packages besides the standard distribution and `advent-of-code`." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "vscode": { - "languageId": "racket" - } - }, - "outputs": [], - "source": [ - "(require racket\n", - " advent-of-code)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "##### Part 1\n", - "\n", - "I built this array as a hashtable with a coordinate struct as the key and the tree height as the value, which makes it easy to check if a particular location falls outside the grid." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "vscode": { - "languageId": "racket" - } - }, - "outputs": [], - "source": [ - "(struct posn (r c) #:transparent)\n", - "\n", - "(define (make-tree-grid data)\n", - " (for*/hash ([(row r) (in-indexed data)] [(col c) (in-indexed (in-string row))])\n", - " (values (posn r c) (string->number (string col)))))\n", - "\n", - "(define tree-grid (make-tree-grid (in-lines (open-aoc-input (find-session) 2022 8))))" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A tree is visible if there aren't any other trees as tall as it or taller blocking the visibility in at least one of the cardinal directions. We check in a particular direction by repeatedly stepping outwards from the original coordinate and checking if we've found a blocking tree yet.\n", - "\n", - "The `for/first` returns `#true` if it finds a blocking tree and `#false` if it runs out of trees to check, and the result is negated to turn it into a predicate about whether the tree can see (and be seen from) outside." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "vscode": { - "languageId": "racket" - } - }, - "outputs": [], - "source": [ - "(define (can-see-to-outside? p height dr dc h)\n", - " (match-define (posn r c) p)\n", - " (not (for/first ([n (in-naturals 1)]\n", - " #:do [(define p* (posn (+ r (* n dr)) (+ c (* n dc))))]\n", - " #:break (not (hash-has-key? h p*))\n", - " #:when (<= height (hash-ref h p*)))\n", - " #true)))" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we need a function to check the four cardinal directions and see if at least one of them is unblocked." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "vscode": { - "languageId": "racket" - } - }, - "outputs": [], - "source": [ - "(define (visible-in-any-direction? p height h)\n", - " (for*/or ([dr (in-list '(-1 0 1))] [dc (in-list '(-1 0 1))] #:when (= 1 (abs (+ dr dc))))\n", - " (can-see-to-outside? p height dr dc h)))" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Finally, add 1 to the count for every tree that's visible:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "vscode": { - "languageId": "racket" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "1733" - ], - "text/plain": [ - "1733" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "(define (count-visible-trees trees)\n", - " (for/sum ([(tree-posn height) (in-hash trees)])\n", - " (cond\n", - " [(visible-in-any-direction? tree-posn height trees) 1]\n", - " [else 0])))\n", - "\n", - "(count-visible-trees tree-grid)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "##### Part 2\n", - "\n", - "Now we're searching for the tree with the most interior visibility, which is the product of how far we can look in each cardinal direction before running out of visible trees. \n", - "\n", - "The search is similar, except now we're calculating a score instead of just checking a predicate. We walk one step at a time, increment the counter and check each tree; if we find a blocking tree, we return the counter, and if we run out of trees to check we break and return the previous counter value. If there aren't any trees in a direction and the `for` body is never evaluated, `for/last` returns `#false`, which is equivalent to a visibility of 0." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "vscode": { - "languageId": "racket" - } - }, - "outputs": [], - "source": [ - "(define (scenic-score-in-direction p height dr dc h)\n", - " (match-define (posn r c) p)\n", - " (define score\n", - " (for/last ([n (in-naturals 1)]\n", - " #:do [(define p* (posn (+ r (* n dr)) (+ c (* n dc))))]\n", - " #:break (not (hash-has-key? h p*))\n", - " #:final (<= height (hash-ref h p*)))\n", - " n))\n", - " (if (not score) 0 score))\n", - "\n", - "(define (scenic-score p height h)\n", - " (for*/product ([dr (in-list '(-1 0 1))] [dc (in-list '(-1 0 1))] #:when (= 1 (abs (+ dr dc))))\n", - " (scenic-score-in-direction p height dr dc h)))" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "With these functions written, calculate each tree's score and find the maximum:" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "vscode": { - "languageId": "racket" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "284648" - ], - "text/plain": [ - "284648" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "(define (find-best-scenic-score trees)\n", - " (apply max\n", - " (for/list ([(tree-posn height) (in-hash trees)])\n", - " (scenic-score tree-posn height trees))))\n", - "\n", - "(find-best-scenic-score tree-grid)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Racket (Trusted)", - "language": "racket", - "name": "racket-trusted" - }, - "language_info": { - "codemirror_mode": "scheme", - "file_extension": ".rkt", - "mimetype": "text/x-racket", - "name": "Racket", - "pygments_lexer": "racket", - "version": "8.7" - }, - "orig_nbformat": 4, - "vscode": { - "interpreter": { - "hash": "916dbcbb3f70747c44a77c7bcd40155683ae19c65e1c03b4aa3499c5328201f1" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/aoc2022/day-08/day-08.rkt b/aoc2022/day-08/day-08.rkt deleted file mode 100644 index 6b60eca..0000000 --- a/aoc2022/day-08/day-08.rkt +++ /dev/null @@ -1,56 +0,0 @@ -#lang racket - -(require advent-of-code) - -(struct posn (r c) #:transparent) - -(define (make-tree-grid data) - (for*/hash ([(row r) (in-indexed data)] [(col c) (in-indexed (in-string row))]) - (values (posn r c) (string->number (string col))))) - -(define tree-grid (make-tree-grid (in-lines (open-aoc-input (find-session) 2022 8)))) - -;; part 1 - -(define (can-see-to-outside? p height dr dc h) - (match-define (posn r c) p) - (not (for/first ([n (in-naturals 1)] - #:do [(define p* (posn (+ r (* n dr)) (+ c (* n dc))))] - #:break (not (hash-has-key? h p*)) - #:when (<= height (hash-ref h p*))) - #true))) - -(define (visible-in-any-direction? p height h) - (for*/or ([dr (in-list '(-1 0 1))] [dc (in-list '(-1 0 1))] #:when (= 1 (abs (+ dr dc)))) - (can-see-to-outside? p height dr dc h))) - -(define (count-visible-trees trees) - (for/sum ([(tree-posn height) (in-hash trees)]) - (cond - [(visible-in-any-direction? tree-posn height trees) 1] - [else 0]))) - -(count-visible-trees tree-grid) - -;; part 2 - -(define (scenic-score-in-direction p height dr dc h) - (match-define (posn r c) p) - (define score - (for/last ([n (in-naturals 1)] - #:do [(define p* (posn (+ r (* n dr)) (+ c (* n dc))))] - #:break (not (hash-has-key? h p*)) - #:final (<= height (hash-ref h p*))) - n)) - (if (not score) 0 score)) - -(define (scenic-score p height h) - (for*/product ([dr (in-list '(-1 0 1))] [dc (in-list '(-1 0 1))] #:when (= 1 (abs (+ dr dc)))) - (scenic-score-in-direction p height dr dc h))) - -(define (find-best-scenic-score trees) - (apply max - (for/list ([(tree-posn height) (in-hash trees)]) - (scenic-score tree-posn height trees)))) - -(find-best-scenic-score tree-grid) diff --git a/aoc2022/day-09/day-09.rkt b/aoc2022/day-09/day-09.rkt deleted file mode 100644 index 0390d2e..0000000 --- a/aoc2022/day-09/day-09.rkt +++ /dev/null @@ -1,76 +0,0 @@ -#lang racket -(require advent-of-code - threading) - -(struct cmd (dir amt)) -(struct posn (x y) #:transparent) - -(define moves - (~> (fetch-aoc-input (find-session) 2022 9) - (string-split "\n") - (map (λ~> (string-split _) - (match _ - [(list dir amt) (cmd (string->symbol dir) (string->number amt))])) - _))) - -(define (move-head p dir) - (match-define (posn x y) p) - (match dir - ['U (posn x (add1 y))] - ['D (posn x (sub1 y))] - ['R (posn (add1 x) y)] - ['L (posn (sub1 x) y)])) - -(define (avg n m) - (/ (+ n m) 2)) - -(define (manhattan-distance p1 p2) - (match-define (posn x1 y1) p1) - (match-define (posn x2 y2) p2) - (+ (abs (- x2 x1)) (abs (- y2 y1)))) - -(define (follow-head head tail) - (match-define (posn hx hy) head) - (match-define (posn tx ty) tail) - - (case (manhattan-distance head tail) - [(0 1) tail] - [(2 4) - (cond - [(and (= 1 (abs (- hx tx)) (abs (- hy ty)))) tail] - [else (posn (avg hx tx) (avg hy ty))])] - [(3) - (cond - [(= 2 (abs (- hx tx))) (posn (avg hx tx) hy)] - [(= 2 (abs (- hy ty))) (posn hx (avg hy ty))])])) - -;; part 1 -(for*/fold ([head (posn 0 0)] [tail (posn 0 0)] [tail-posns (set)] #:result (set-count tail-posns)) - ([move (in-list moves)] #:do [(match-define (cmd dir amt) move)] [_ (in-range amt)]) - (define new-head (move-head head dir)) - (define new-tail (follow-head new-head tail)) - (values new-head new-tail (set-add tail-posns new-tail))) - -;; part 2 -(for*/fold ([knots (make-list 10 (posn 0 0))] [tail-posns (set)] #:result (set-count tail-posns)) - ([move (in-list moves)] #:do [(match-define (cmd dir amt) move)] [_ (in-range amt)]) - (define updated-knots - (for/fold ([knots-list (list (move-head (first knots) dir))]) - ([following-knot (in-list (rest knots))]) - (cons (follow-head (car knots-list) following-knot) knots-list))) - (values (reverse updated-knots) (set-add tail-posns (first updated-knots)))) - -;; refactor: part 1 and 2 combined -(define (follow-tail move-list rope-length) - (for*/fold ([knots (make-list rope-length (posn 0 0))] - [tail-posns (set)] - #:result (set-count tail-posns)) - ([move (in-list move-list)] #:do [(match-define (cmd dir amt) move)] [_ (in-range amt)]) - (define updated-knots - (for/fold ([knots-list (list (move-head (first knots) dir))]) - ([following-knot (in-list (rest knots))]) - (cons (follow-head (car knots-list) following-knot) knots-list))) - (values (reverse updated-knots) (set-add tail-posns (first updated-knots))))) - -(time (follow-tail moves 2)) -(time (follow-tail moves 10)) diff --git a/aoc2022/day-10/day-10.rkt b/aoc2022/day-10/day-10.rkt deleted file mode 100644 index 70c80d3..0000000 --- a/aoc2022/day-10/day-10.rkt +++ /dev/null @@ -1,43 +0,0 @@ -#lang racket - -(require advent-of-code - threading - fancy-app - (only-in algorithms chunks-of)) - -(define/match (process-instruction _) - [((list "noop")) (list 'noop)] - [((list "addx" (app string->number val))) (list 'noop (cons 'addx val))]) - -(define instructions - (~> (fetch-aoc-input (find-session) 2022 10) - (string-split "\n") - (map (λ~> string-split process-instruction) _) - (apply append _))) - -;; part 1 -(define interesting-times (inclusive-range 20 220 40)) - -(define/match (evaluate-instruction _op acc) - [('noop _) acc] - [((cons 'addx n) _) (+ acc n)]) - -(for/fold ([acc 1] [interesting-strengths 0] #:result interesting-strengths) - ([inst (in-list instructions)] [i (in-naturals 1)]) - (define new-interesting - (if (member i interesting-times) (+ interesting-strengths (* i acc)) interesting-strengths)) - (values (evaluate-instruction inst acc) new-interesting)) - -;; part 2 -(for/fold ([acc 1] [pixels '()] #:result (~> pixels reverse (chunks-of 40) (map (apply string _) _))) - ([inst (in-list instructions)] [i (in-naturals)]) - (define new-pixel (if (member (modulo i 40) (list (sub1 acc) acc (add1 acc))) #\█ #\space)) - (values (evaluate-instruction inst acc) (cons new-pixel pixels))) - -; for my data set, -; '("███ ████ ████ █ █ ████ ████ █ █ ██ " -; "█ █ █ █ █ █ █ █ █ █ █ █ " -; "█ █ █ ███ ██ ███ ███ ████ █ █ " -; "███ █ █ █ █ █ █ █ █ ████ " -; "█ █ █ █ █ █ █ █ █ █ █ █ " -; "█ █ ████ ████ █ █ ████ █ █ █ █ █ ") diff --git a/aoc2022/day-11/day-11.rkt b/aoc2022/day-11/day-11.rkt deleted file mode 100644 index af7b4ee..0000000 --- a/aoc2022/day-11/day-11.rkt +++ /dev/null @@ -1,85 +0,0 @@ -#lang racket -(require threading) - -(struct monkey ([items #:mutable] operation modulus yes no)) - -;; really don't feel like parsing the input today -(define monkeys-list - (list (cons 0 (monkey '(97 81 57 57 91 61) - (λ (n) (* n 7)) - 11 5 6)) - (cons 1 (monkey '(88 62 68 90) - (λ (n) (* n 17)) - 19 4 2)) - (cons 2 (monkey '(74 87) - (λ (n) (+ n 2)) - 5 7 4)) - (cons 3 (monkey '(53 81 60 87 90 99 75) - (λ (n) (+ n 1)) - 2 2 1)) - (cons 4 (monkey '(57) - (λ (n) (+ n 6)) - 13 7 0)) - (cons 5 (monkey '(54 84 91 55 59 72 75 70) - (λ (n) (* n n)) - 7 6 3)) - (cons 6 (monkey '(95 79 79 68 78) - (λ (n) (+ n 3)) - 3 1 3)) - (cons 7 (monkey '(61 97 67) - (λ (n) (+ n 4)) - 17 0 5)))) - -(define monkey-lcm (~> monkeys-list (map (λ~> cdr monkey-modulus) _) (apply lcm _))) - -;; part 1 -(define monkeys-count-pt1 (make-hash)) -(define monkeys-hash-pt1 (make-hash monkeys-list)) - -(for - ([rnd (in-range 20)]) - (for - ([turn (inclusive-range 0 7)]) - (match-define (monkey items op modulus yes no) (hash-ref monkeys-hash-pt1 turn)) - (for ([i (in-list items)]) - (define i* (quotient (op i) 3)) - (define m (if (= 0 (modulo i* modulus)) yes no)) - (match-define (monkey items* op* modulus* yes* no*) (hash-ref monkeys-hash-pt1 m)) - (hash-update! monkeys-count-pt1 turn add1 0) - (hash-set! monkeys-hash-pt1 - m - (monkey (append items* (list i*)) op* modulus* yes* no*))) - (hash-set! monkeys-hash-pt1 turn (monkey '() op modulus yes no)))) - -(~> monkeys-count-pt1 - hash->list - (sort _ > #:key cdr) - (take _ 2) - (map cdr _) - (apply * _)) - -;; part 2 -(define monkeys-count-pt2 (make-hash)) -(define monkeys-hash-pt2 (make-hash monkeys-list)) - -(for - ([rnd (in-range 10000)]) - (for - ([turn (inclusive-range 0 7)]) - (match-define (monkey items op modulus yes no) (hash-ref monkeys-hash-pt2 turn)) - (for ([i (in-list items)]) - (define i* (op i)) - (define m (if (= 0 (modulo i* modulus)) yes no)) - (match-define (monkey items* op* modulus* yes* no*) (hash-ref monkeys-hash-pt2 m)) - (hash-update! monkeys-count-pt2 turn add1 0) - (hash-set! monkeys-hash-pt2 - m - (monkey (append items* (list (remainder i* monkey-lcm))) op* modulus* yes* no*))) - (hash-set! monkeys-hash-pt2 turn (monkey '() op modulus yes no)))) - -(~> monkeys-count-pt2 - hash->list - (sort _ > #:key cdr) - (take _ 2) - (map cdr _) - (apply * _)) \ No newline at end of file diff --git a/aoc2022/day-12/day-12.rkt b/aoc2022/day-12/day-12.rkt deleted file mode 100644 index c3f01ac..0000000 --- a/aoc2022/day-12/day-12.rkt +++ /dev/null @@ -1,46 +0,0 @@ -#lang racket - -(require advent-of-code - graph) - -(define raw-terrain (fetch-aoc-input (find-session) 2022 12 #:cache #true)) -(define special-points (make-hash)) - -(define terrain-mesh - (for*/hash ([(row x) (in-indexed (string-split raw-terrain))] [(col y) (in-indexed row)]) - (define p (cons x y)) - (case col - [(#\S) - (hash-set! special-points 'start p) - (values p 0)] - [(#\E) - (hash-set! special-points 'end p) - (values p 25)] - [else (values p (- (char->integer col) (char->integer #\a)))]))) - -(define (neighbors p) - (match-define (cons x y) p) - (for*/list ([dx (in-list '(-1 0 1))] - [dy (in-list '(-1 0 1))] - #:when (= 1 (abs (+ dx dy))) - #:do [(define p* (cons (+ x dx) (+ y dy)))] - #:when (hash-has-key? terrain-mesh p*)) - p*)) - -(define paths - (directed-graph (for*/list ([p (in-list (hash-keys terrain-mesh))] - [p* (in-list (neighbors p))] - #:unless (> (sub1 (hash-ref terrain-mesh p*)) - (hash-ref terrain-mesh p))) - (list p p*)))) - -;; part 1 -(time (match-define-values (distances _) (bfs paths (hash-ref special-points 'start))) - (hash-ref distances (hash-ref special-points 'end))) - -;; part 2 -(time (for/lists - (lengths #:result (apply min lengths)) - ([start (in-list (hash-keys terrain-mesh))] #:when (= 0 (hash-ref terrain-mesh start))) - (match-define-values (distances _) (bfs paths start)) - (hash-ref distances (hash-ref special-points 'end)))) diff --git a/aoc2022/day-13/day-13.rkt b/aoc2022/day-13/day-13.rkt deleted file mode 100644 index 39435e9..0000000 --- a/aoc2022/day-13/day-13.rkt +++ /dev/null @@ -1,28 +0,0 @@ -#lang racket - -(require advent-of-code) - -(define raw-packets - (parameterize ([current-readtable (make-readtable #f #\, #\space #f)]) - (port->list read (open-aoc-input (find-session) 2022 13 #:cache #true)))) - -(define (compare xs ys) - (match* (xs ys) - [('() (list* _)) #true] - [((list* _) '()) #false] - [((list* _same x-rest) (list* _same y-rest)) (compare x-rest y-rest)] - [((list* (? integer? x) _) (list* (? integer? y) _)) (< x y)] - [((list* (? list? xs*) _) (list* (? list? ys*) _)) (compare xs* ys*)] - [(xs (list* (? integer? y) y-rest)) (compare xs (cons (list y) y-rest))] - [((list* (? integer? x) x-rest) ys) (compare (cons (list x) x-rest) ys)])) - -;; part 1 -(for/sum ([i (in-naturals 1)] [packet (in-slice 2 raw-packets)] #:when (apply compare packet)) i) - -;; part 2 -(define divider-packets (list '((2)) '((6)))) -(define amended-packets (append divider-packets raw-packets)) - -(for/product ([i (in-naturals 1)] [packet (in-list (sort amended-packets compare))] - #:when (member packet divider-packets)) - i) diff --git a/aoc2022/day-14/day-14.rkt b/aoc2022/day-14/day-14.rkt deleted file mode 100644 index 88ba297..0000000 --- a/aoc2022/day-14/day-14.rkt +++ /dev/null @@ -1,51 +0,0 @@ -#lang racket - -(require advent-of-code - threading - algorithms) - -(define data (fetch-aoc-input (find-session) 2022 14 #:cache #true)) - -(define (trace-line-between-points p1 p2) - (match* (p1 p2) - [((list x y1) (list x y2)) (map (λ (y) (cons x y)) (inclusive-range (min y1 y2) (max y1 y2)))] - [((list x1 y) (list x2 y)) (map (λ (x) (cons x y)) (inclusive-range (min x1 x2) (max x1 x2)))])) - -(define (find-points-in-structure str) - (define endpoints - (for/list ([coord-pair (in-list (string-split str " -> "))]) - (for/list ([coord (in-list (string-split coord-pair ","))]) - (string->number coord)))) - (~>> endpoints (adjacent-map trace-line-between-points) (apply append) (list->set))) - -(define blocked-locations - (~> data (string-split "\n") (map find-points-in-structure _) (apply set-union _))) - -(define max-vertical-distance (~>> blocked-locations (set->list) (argmax cdr) cdr add1)) - -(define (open? pts p) - (not (set-member? pts p))) - -;; part 1 -(define (trace-grain pts path #:at-limit do-at-limit) - (match-define (list* (and p (cons x y)) _) path) - (match-define (list dest-1 dest-2 dest-3) (map (λ (d) (cons (+ x d) (add1 y))) '(0 -1 1))) - (cond - [(>= y max-vertical-distance) (values (do-at-limit pts p) path)] - [(open? pts dest-1) (trace-grain pts (cons dest-1 path) #:at-limit do-at-limit)] - [(open? pts dest-2) (trace-grain pts (cons dest-2 path) #:at-limit do-at-limit)] - [(open? pts dest-3) (trace-grain pts (cons dest-3 path) #:at-limit do-at-limit)] - [else (values (set-add pts (car path)) path)])) - -(time (for/fold ([pts blocked-locations] [path (list (cons 500 0))] [grains 0] #:result grains) - ([_ (in-naturals 1)]) - (define-values (pts* path*) (trace-grain pts path #:at-limit (const 'break))) - #:break (equal? pts* 'break) - (values pts* (cdr path*) (add1 grains)))) - -;; part 2 -(time (for/fold ([pts blocked-locations] [path (list (cons 500 0))] [grains 0] #:result grains) - ([_ (in-naturals 1)]) - #:break (not (open? pts (cons 500 0))) - (define-values (pts* path*) (trace-grain pts path #:at-limit set-add)) - (values pts* (cdr path*) (add1 grains)))) diff --git a/aoc2022/day-15/day-15.rkt b/aoc2022/day-15/day-15.rkt deleted file mode 100644 index b050807..0000000 --- a/aoc2022/day-15/day-15.rkt +++ /dev/null @@ -1,54 +0,0 @@ -#lang racket - -(require advent-of-code - threading - fancy-app - algorithms - (prefix-in iset- data/integer-set)) - -(struct beacon-record (sensor beacon)) -(struct posn (x y)) - -(define beacon-records - (~> (fetch-aoc-input (find-session) 2022 15 #:cache #true) - (string-split "\n") - (map (λ~> (string-replace #px"[^\\d\\s-]" "") - string-split - (map string->number _) - (chunks-of 2) - (map (apply posn _) _) - (apply beacon-record _)) - _))) - -(define (manhattan-distance-to-beacon record) - (match-define (beacon-record (posn sx sy) (posn bx by)) record) - (+ (abs (- sx bx)) (abs (- sy by)))) - -(define (coverage-at-row record row) - (match-define (beacon-record (posn sx sy) _) record) - (define x-distance (- (manhattan-distance-to-beacon record) (abs (- row sy)))) - (cond - [(negative? x-distance) (iset-make-range)] - [else (iset-make-range (- sx x-distance) (+ sx x-distance))])) - -(define (total-coverage-at-row records row) - (for/fold ([coverage (iset-make-range)]) ([r (in-list records)]) - (iset-union coverage (coverage-at-row r row)))) - -;; part 1 -(define (coverage-without-beacons records row) - (~> (total-coverage-at-row records row) - iset-count - (- (count (λ (b) (= row (posn-y b))) - (~> beacon-records (map beacon-record-beacon _) remove-duplicates))))) - -(coverage-without-beacons beacon-records 2000000) - -;; part 2 -(define (find-only-beacon beacon-records size) - (for/first ([y (in-range 0 size)] - #:do [(define xs (iset-complement (total-coverage-at-row beacon-records y) 0 size))] - #:when (= 1 (iset-count xs))) - (+ (* 4000000 (iset-get-integer xs)) y))) - -(find-only-beacon beacon-records 4000000) diff --git a/aoc2022/day-16/day-16.rkt b/aoc2022/day-16/day-16.rkt deleted file mode 100644 index 5ec56d6..0000000 --- a/aoc2022/day-16/day-16.rkt +++ /dev/null @@ -1,107 +0,0 @@ -#lang racket - -(require advent-of-code - fancy-app - graph - threading) - -(define (process-line str) - (match str - [(regexp #px"Valve (\\w\\w) has flow rate=(\\d+); tunnels? leads? to valves? (.+)" - (list _ name (app string->number rate) (app (string-split _ ", ") valves))) - (list name rate valves)])) - -(define cave-data - (~> (fetch-aoc-input (find-session) 2022 16 #:cache #true) - (string-split "\n") - (map process-line _))) - -(define cave-map - (for*/lists (tunnels #:result (directed-graph tunnels)) - ([valve (in-list cave-data)] #:do [(match-define (list name _ valves) valve)] - [destination (in-list valves)]) - (list name destination))) - -(define valve-flows - (for/hash ([valve (in-list cave-data)] - #:do [(match-define (list name flow _) valve)] - #:when (> flow 0)) - (values name flow))) - -(define shortest-path-lengths (johnson cave-map)) - -(define (reachable-destinations start dests minutes-left) - (for/list ([(dest _) (in-hash dests)] - #:do [(define travel-time - (hash-ref shortest-path-lengths (list start dest) minutes-left))] - #:when (<= 1 travel-time minutes-left)) - (cons dest travel-time))) - -;; part 1 -(define (find-best-single-route start - [minutes-left 30] - [current-pressure 0] - [available-valves valve-flows]) - (cond - [(>= minutes-left 1) - (for/fold ([running-pressure current-pressure] - [remaining-valves available-valves] - #:result (cons running-pressure remaining-valves)) - ([candidate (reachable-destinations start available-valves minutes-left)]) - (match-define (cons dest travel-time) candidate) - (define minutes-left* (- minutes-left (add1 travel-time))) - (match-define (cons candidate-pressure remaining-valves*) - (find-best-single-route dest - minutes-left* - (+ current-pressure (* (hash-ref valve-flows dest) minutes-left*)) - (hash-remove available-valves dest))) - (if (> candidate-pressure running-pressure) - (values candidate-pressure remaining-valves*) - (values running-pressure remaining-valves*)))] - [else (cons current-pressure available-valves)])) - -(car (find-best-single-route "AA")) - -;; part 2 - -(define (possible-paths start dests minutes-left) - (cond - [(or (hash-empty? dests) (< minutes-left 3)) '()] - [else - (for/fold ([path '()]) ([dest (in-list (reachable-destinations start dests minutes-left))]) - (match-define (cons valve minutes) dest) - (define dests* (hash-remove dests valve)) - (define next-valves (possible-paths valve dests* (- minutes-left minutes))) - (append (list (list dest)) (map (cons dest _) next-valves) path))])) - -(define (flow-for-path path minutes [sum 0]) - (match path - ['() sum] - [(list* (cons valve dist) tail) - (define valve-open-for (- minutes dist 1)) - (flow-for-path tail valve-open-for (+ sum (* (hash-ref valve-flows valve) valve-open-for)))])) - -(define minutes-left 26) - -(define human-paths - (~>> (possible-paths "AA" valve-flows minutes-left) - (map (λ (path) (cons (flow-for-path path minutes-left) (map car path)))) - (sort _ > #:key car))) - -(define (best-possible-elephant-path human-path) - (define remaining-dests - (for/hash ([(dest flow) (in-hash valve-flows)] #:unless (member dest (cdr human-path))) - (values dest flow))) - (~>> (possible-paths "AA" remaining-dests minutes-left) - (map (λ (path) (cons (flow-for-path path minutes-left) (map car path)))) - (sort _ > #:key car) - car)) - -;; this takes a long time to run but I stuck a displayln in for debugging -;; and just took the highest max-flow after letting it run for a while and waiting until -;; it stopped printing new bests to console -(for*/fold ([max-flow 0]) - ([human-path (in-list human-paths)] - #:do [(define elephant-path (best-possible-elephant-path human-path))]) - (define combined-flow (+ (car human-path) (car elephant-path))) - (if (< max-flow combined-flow) combined-flow max-flow)) diff --git a/aoc2022/day-17/day-17.rkt b/aoc2022/day-17/day-17.rkt deleted file mode 100644 index 28e8763..0000000 --- a/aoc2022/day-17/day-17.rkt +++ /dev/null @@ -1,53 +0,0 @@ -#lang racket - -(require advent-of-code - threading - fancy-app - data/collection) - -(define (relative-rock-coordinates rock) - (for*/hash ([(row y) (in-indexed (reverse rock))] [(_col x) (in-indexed row)]) - (values (cons x y) #true))) - -(define rock-shapes - (~> (open-input-file "./2022/day-17/rock-shapes") - port->string - (string-split "\n\n") - (map (λ~> (string-split _ "\n") (map string->list _) relative-rock-coordinates) _))) - -(define gusts - (~> (fetch-aoc-input (find-session) 2022 17 #:cache #true) - string-trim - string->list - (map (match-lambda - [#\> 1] - [#\< -1]) - _))) - -(define (place-new-rock rock elevation) - (for/hash ([(posn _) (in-hash rock)]) - (match-define (cons x y) posn) - (values (cons (+ x 3) (+ y elevation 4)) #true))) - -(define (gust-move rock wind cave) - (define moved-rock - (for/hash ([(posn _) (in-hash rock)]) - (match-define (cons x y) posn) - (define posn* (cons (+ x wind) y)) - (if (hash-has-key? cave posn) (values 'collision #t) (values posn* #t)))) - (if (hash-has-key? moved-rock 'collision) rock moved-rock)) - -(for/fold ([cave (hash)] - [falling-rock #f] - [top-of-rocks 0] - [rock-cycle (cycle rock-shapes)] - [rock-number 0] - #:result top-of-rocks) - (#:break (> rock-number 2022) [gust (in-cycle gusts)]) - (cond - [(not falling-rock) - (values cave - (~> (first rock-cycle) (place-new-rock top-of-rocks) (gust-move gust cave)) - top-of-rocks - (rest rock-cycle) - (add1 rock-number))])) diff --git a/aoc2022/day-17/rock-shapes b/aoc2022/day-17/rock-shapes deleted file mode 100644 index fbcc382..0000000 --- a/aoc2022/day-17/rock-shapes +++ /dev/null @@ -1,17 +0,0 @@ -#### - -.#. -### -.#. - -..# -..# -### - -# -# -# -# - -## -## \ No newline at end of file diff --git a/aoc2022/day-18/day-18.rkt b/aoc2022/day-18/day-18.rkt deleted file mode 100644 index 157784d..0000000 --- a/aoc2022/day-18/day-18.rkt +++ /dev/null @@ -1,57 +0,0 @@ -#lang racket - -(require advent-of-code - relation - threading - graph) - -(define positions (~> (fetch-aoc-input (find-session) 2022 18 #:cache #true) (string-split "\n"))) - -(struct posn (x y z) #:transparent) - -(define cubes - (for/list ([cube (in-list positions)]) - (match (string-split cube ",") - [(list (app ->number x) (app ->number y) (app ->number z)) (posn x y z)]))) - -(define cubes-set (list->set cubes)) - -(define (neighbors p) - (match-define (posn x y z) p) - (for*/list ([dx '(-1 0 1)] - [dy '(-1 0 1)] - [dz '(-1 0 1)] - #:when (= 1 (+ (abs dx) (abs dy) (abs dz)))) - (posn (+ x dx) (+ y dy) (+ z dz)))) - -;; part 1 - -(for*/sum ([cube (in-set cubes-set)] - [neighbor (in-list (neighbors cube))] - #:unless (set-member? cubes-set neighbor)) - 1) - -;; part 2 -(define max-x (~> cubes (apply max _ #:key posn-x) posn-x (+ 2))) -(define max-y (~> cubes (apply max _ #:key posn-y) posn-y (+ 2))) -(define max-z (~> cubes (apply max _ #:key posn-z) posn-z (+ 2))) - -(define air-set - (for*/set ([x (in-inclusive-range -1 max-x)] - [y (in-inclusive-range -1 max-y)] - [z (in-inclusive-range -1 max-z)] - #:do [(define p (posn x y z))] - #:unless (set-member? cubes-set p)) - p)) - -(define air-graph - (for*/lists (ps #:result (undirected-graph ps)) - ([a (in-set air-set)] - [neighbor (in-list (neighbors a))] - #:when (set-member? air-set neighbor)) - (list a neighbor))) - -(for*/sum ([air (in-set (~> air-graph cc (sort > #:key length _) car))] - [neighbor (in-list (neighbors air))] - #:when (set-member? cubes-set neighbor)) - 1) diff --git a/aoc2022/day-19/day-19.rkt b/aoc2022/day-19/day-19.rkt deleted file mode 100644 index 1400bf2..0000000 --- a/aoc2022/day-19/day-19.rkt +++ /dev/null @@ -1,11 +0,0 @@ -#lang racket - -(require advent-of-code - threading) - -(struct blueprint (id ore clay obsidian geode)) - -(define (parse-line str) - (match (~> str (string-replace #px"[^\\d\\s]" "") string-split) - [(list id ore clay obsidian-ore obsidian-clay geode-ore geode-obsidian) - (blueprint id ore clay (cons obsidian-ore obsidian-clay) (cons geode-ore geode-obsidian))])) diff --git a/aoc2022/day-20/day-20.rkt b/aoc2022/day-20/day-20.rkt deleted file mode 100644 index 6dd1070..0000000 --- a/aoc2022/day-20/day-20.rkt +++ /dev/null @@ -1,48 +0,0 @@ -#lang racket -(require advent-of-code - threading) - -(define data (port->list read (open-aoc-input (find-session) 2022 20 #:cache #true))) - -(define gps-lst data) -(define gps-len (length gps-lst)) -(define gps-indexed (map cons (inclusive-range 1 gps-len) gps-lst)) - -(define (mix pt data) - (match-define (list left ... (== pt) right ...) data) - (define start (index-of data pt)) - (define move-by (modulo (cdr pt) (sub1 gps-len))) - (cond - [(= 0 move-by) data] - [(<= move-by (length right)) - (match-define-values (new-left new-right) - (split-at (append left right) (modulo (+ move-by start) (sub1 gps-len)))) - (append new-left (list pt) new-right)] - [else - (match-define-values (new-left new-right) - (split-at (append left right) (modulo (+ move-by start) (sub1 gps-len)))) - (append new-left (list pt) new-right)])) - -(define (mix-gps data original) - (for/fold ([pts data]) ([pt original]) - (mix pt pts))) - -(define (cycle-mixed-gps mixed) - (define lst (map cdr mixed)) - (in-sequences (drop lst (index-of lst 0)) (in-cycle lst))) - -(define (calculate-answer seq) - (for/sum ([id '(1000 2000 3000)]) (sequence-ref seq id))) - -;; part 1 -(~> gps-indexed (mix-gps _ gps-indexed) cycle-mixed-gps calculate-answer) - -;; part 2 -(define encrypted-gps-indexed - (for/list ([pt (in-list gps-indexed)] #:do [(match-define (cons i v) pt)]) - (cons i (* 811589153 v)))) - -(~>> encrypted-gps-indexed - ((λ (data) (foldr (λ (_ pts) (mix-gps pts data)) data (range 10)))) - cycle-mixed-gps - calculate-answer) diff --git a/aoc2022/day-21/day-21.rkt b/aoc2022/day-21/day-21.rkt deleted file mode 100644 index fccd6ad..0000000 --- a/aoc2022/day-21/day-21.rkt +++ /dev/null @@ -1,43 +0,0 @@ -#lang racket - -(require advent-of-code - (only-in relation ->number ->symbol)) - -(struct monkey (name op) #:transparent) -(struct op (f first second) #:transparent) - -(define (parse-monkey str) - (match (string-split str " ") - [(list (app (curryr string-trim ":") name) name1 (app ->symbol f) name2) - (monkey name (op f name1 name2))] - [(list (app (curryr string-trim ":") name) (app ->number int)) - (monkey name (op 'constant int #f))])) - -(define raw-monkeys (port->lines (open-aoc-input (find-session) 2022 21 #:cache #true))) - -(define monkey-table - (for/hash ([m raw-monkeys] #:do [(match-define (monkey name op) (parse-monkey m))]) - (values name op))) - -;; part 1 -(define (evaluate-monkey m-name [guess #f]) - (match-define (op f name1 name2) - (if (and guess (equal? m-name "humn")) (op 'constant guess #f) (hash-ref monkey-table m-name))) - (match f - ['constant name1] - ['+ (+ (evaluate-monkey name1 guess) (evaluate-monkey name2 guess))] - ['- (- (evaluate-monkey name1 guess) (evaluate-monkey name2 guess))] - ['* (* (evaluate-monkey name1 guess) (evaluate-monkey name2 guess))] - ['/ (/ (evaluate-monkey name1 guess) (evaluate-monkey name2 guess))])) - -(evaluate-monkey "root") - -;; part 2 -;; since humn only ever appears once, and it's never the divisor in a division operation, -;; the difference of the branches is linearly proportional to humn -;; therefore, if we find two points we can calculate the root directly -(match-define (op _ branch-1 branch-2) (hash-ref monkey-table "root")) -(define known-side (evaluate-monkey branch-2)) -(define humn-zero (- known-side (evaluate-monkey branch-1 0))) -(define humn-one (- known-side (evaluate-monkey branch-1 1))) -(- (/ humn-zero (- humn-one humn-zero))) diff --git a/aoc2022/day-22/day-22.rkt b/aoc2022/day-22/day-22.rkt deleted file mode 100644 index bcce5f8..0000000 --- a/aoc2022/day-22/day-22.rkt +++ /dev/null @@ -1,35 +0,0 @@ -#lang racket - -(require advent-of-code - threading) - -(struct posn (x y) #:transparent) - -(match-define (list raw-map raw-instructions) - (string-split (fetch-aoc-input (find-session) 2022 22 #:cache #true) "\n\n")) - -(define board-map - (for*/hash ([(row y) (in-indexed (string-split raw-map "\n"))] - [(col x) (in-indexed row)] - #:unless (equal? col #\space)) - (define tile - (match col - [#\. 'open] - [#\# 'wall])) - (values (posn x y) tile))) - -(define instructions - (~>> raw-instructions - (regexp-match* #px"(\\d+)|R|L") - (map (match-lambda - [(? string->number n) (string->number n)] - ["R" 'clockwise] - ["L" 'counterclockwise])))) - -(define start-tile - (~>> board-map - hash-keys - (filter (match-lambda - [(posn _ 0) #true] - [_ #false])) - (argmin posn-x))) \ No newline at end of file diff --git a/aoc2022/day-23/day-23.rkt b/aoc2022/day-23/day-23.rkt deleted file mode 100644 index 6069859..0000000 --- a/aoc2022/day-23/day-23.rkt +++ /dev/null @@ -1,76 +0,0 @@ -#lang racket - -(require advent-of-code - fancy-app - threading) - -(struct posn (x y) #:transparent) - -(define initial-map - (for*/hash ([(row y) (in-indexed (in-lines (open-aoc-input (find-session) 2022 23 #:cache #true)))] - [(col x) (in-indexed row)] - #:when (equal? col #\#)) - (values (posn x y) #t))) - -(define/match (neighbors-in direction p) - [('north (posn x (app sub1 y*))) (list (posn (sub1 x) y*) (posn x y*) (posn (add1 x) y*))] - [('south (posn x (app add1 y*))) (list (posn (sub1 x) y*) (posn x y*) (posn (add1 x) y*))] - [('east (posn (app add1 x*) y)) (list (posn x* (add1 y)) (posn x* y) (posn x* (sub1 y)))] - [('west (posn (app sub1 x*) y)) (list (posn x* (add1 y)) (posn x* y) (posn x* (sub1 y)))]) - -(define/match (move-to direction p) - [('stay p) p] - [('north (posn x y)) (posn x (sub1 y))] - [('south (posn x y)) (posn x (add1 y))] - [('east (posn x y)) (posn (add1 x) y)] - [('west (posn x y)) (posn (sub1 x) y)]) - -(define (propose-movements elves dirs) - (for/hash ([(elf _) (in-hash elves)]) - (define dir-candidates - (for/list ([dir dirs] - #:do [(define neighbors (neighbors-in dir elf))] - #:unless (ormap (curry hash-has-key? elves) neighbors)) - dir)) - (define chosen-dir - (match dir-candidates - ['() 'stay] - [(== dirs) 'stay] - [(cons dir _) dir])) - (values elf chosen-dir))) - -(define (try-proposed-movements elves) - (define moved-elves (make-hash)) - (for ([(elf dir) (in-hash elves)]) - (hash-update! moved-elves (move-to dir elf) (cons elf _) '())) - (define reconciled-elves (make-hash)) - (for ([(posn elves) (in-hash moved-elves)]) - (match elves - ; if there's only one elf at a coordinate, leave it there - [(list _) (hash-set! reconciled-elves posn #t)] - ; if there's many elves at one coordinate, back them up to their previous spot - [many-elves - (for ([elf (in-list many-elves)]) - (hash-set! reconciled-elves elf #t))])) - reconciled-elves) - -;; part 1 -(define (count-empty-spots elves) - (define elf-posns (hash-keys elves)) - (match-define (list x-min _ ... x-max) (sort (map posn-x elf-posns) <)) - (match-define (list y-min _ ... y-max) (sort (map posn-y elf-posns) <)) - (for*/sum ([y (inclusive-range y-min y-max)] [x (inclusive-range x-min x-max)]) - (if (hash-has-key? elves (posn x y)) 0 1))) - -(for/fold ([elves initial-map] [dirs '(north south west east)] #:result (count-empty-spots elves)) - ([_rnd (in-range 10)]) - (values (~> elves (propose-movements dirs) try-proposed-movements) - (append (cdr dirs) (list (car dirs))))) - -;; part 2 -(for/fold ([elves initial-map] [dirs '(north south west east)] [rnd 1] #:result rnd) - ([_rnd (in-naturals)]) - (define elves-proposed (propose-movements elves dirs)) - ; elves have stopped moving if they all conclude they want to stay put - #:break (~> elves-proposed hash-values remove-duplicates (equal? '(stay))) - (values (try-proposed-movements elves-proposed) (append (cdr dirs) (list (car dirs))) (add1 rnd))) diff --git a/aoc2022/day-25/day-25.rkt b/aoc2022/day-25/day-25.rkt deleted file mode 100644 index 078cef4..0000000 --- a/aoc2022/day-25/day-25.rkt +++ /dev/null @@ -1,24 +0,0 @@ -#lang racket -(require advent-of-code - threading) - -(define fuel-requirements (port->lines (open-aoc-input (find-session) 2022 25 #:cache #true))) - -(define (snafu->decimal snafu) - (for/sum ([digit (in-list (~> snafu string->list reverse))] [place (in-naturals)]) - (define value - (match digit - [#\= -2] - [#\- -1] - [c (~> c string string->number)])) - (* value (expt 5 place)))) - -(define (decimal->snafu n [acc '()]) - (match n - [0 (list->string acc)] - [n (decimal->snafu (quotient (+ n 2) 5) (cons (string-ref "012=-" (modulo n 5)) acc))])) - -;; part 1 -(~> (for/sum ([fuel (in-list fuel-requirements)]) (snafu->decimal fuel)) decimal->snafu) - -;; no part 2 メリークリスマス \ No newline at end of file diff --git a/aoc2023-gleam/.DS_Store b/aoc2023-gleam/.DS_Store deleted file mode 100644 index 5172429..0000000 Binary files a/aoc2023-gleam/.DS_Store and /dev/null differ diff --git a/aoc2023-gleam/.github/workflows/test.yml b/aoc2023-gleam/.github/workflows/test.yml deleted file mode 100644 index cf2096e..0000000 --- a/aoc2023-gleam/.github/workflows/test.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: test - -on: - push: - branches: - - master - - main - pull_request: - -jobs: - test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: erlef/setup-beam@v1 - with: - otp-version: "26.0.2" - gleam-version: "0.32.4" - rebar3-version: "3" - # elixir-version: "1.15.4" - - run: gleam deps download - - run: gleam test - - run: gleam format --check src test diff --git a/aoc2023-gleam/.gitignore b/aoc2023-gleam/.gitignore deleted file mode 100644 index 8248306..0000000 --- a/aoc2023-gleam/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -*.beam -*.ez -build -erl_crash.dump - -aoc.toml \ No newline at end of file diff --git a/aoc2023-gleam/README.md b/aoc2023-gleam/README.md deleted file mode 100644 index 3f534e8..0000000 --- a/aoc2023-gleam/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# aoc2023 - -[![Package Version](https://img.shields.io/hexpm/v/aoc2023)](https://hex.pm/packages/aoc2023) -[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/aoc2023/) - -## Quick start - -```sh -gleam run # Run the project -gleam test # Run the tests -gleam shell # Run an Erlang shell -``` - -## Installation - -If available on Hex this package can be added to your Gleam project: - -```sh -gleam add aoc2023 -``` - -and its documentation can be found at . - -## Use - -* Set up a solution: `gleam run -m adglent/day ` -* Check against examples: `gleam test -- --modules=day/day_test` -* Get final answer: `gleam run -m day/solve

` \ No newline at end of file diff --git a/aoc2023-gleam/gleam.toml b/aoc2023-gleam/gleam.toml deleted file mode 100644 index 8190aef..0000000 --- a/aoc2023-gleam/gleam.toml +++ /dev/null @@ -1,22 +0,0 @@ -name = "aoc2023" -version = "0.1.0" -gleam = ">= 0.33.0" - -# Fill out these fields if you intend to generate HTML documentation or publish -# your project to the Hex package manager. -# -# description = "" -# licences = ["Apache-2.0"] -# repository = { type = "github", user = "username", repo = "project" } -# links = [{ title = "Website", href = "https://gleam.run" }] - -[dependencies] -gleam_stdlib = "~> 0.33" -simplifile = "~> 1.0" -gleam_erlang = "~> 0.23" -gleam_community_maths = "~> 1.0" -gleam_otp = "~> 0.8" -pqueue = "~> 2.0" - -[dev-dependencies] -adglent = "~> 1.2" diff --git a/aoc2023-gleam/manifest.toml b/aoc2023-gleam/manifest.toml deleted file mode 100644 index 416a155..0000000 --- a/aoc2023-gleam/manifest.toml +++ /dev/null @@ -1,29 +0,0 @@ -# This file was generated by Gleam -# You typically do not need to edit this file - -packages = [ - { name = "adglent", version = "1.2.0", build_tools = ["gleam"], requirements = ["gap", "gleam_community_ansi", "gleam_erlang", "gleam_http", "gleam_httpc", "gleam_otp", "gleam_stdlib", "glint", "simplifile", "snag", "tom"], otp_app = "adglent", source = "hex", outer_checksum = "A20D35001061F8AD602E3B92FB3AC0E1E4EEC642AD2AAE0ACEAD3A85F37DA7F0" }, - { name = "gap", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_community_ansi", "gleam_stdlib"], otp_app = "gap", source = "hex", outer_checksum = "2EE1B0A17E85CF73A0C1D29DA315A2699117A8F549C8E8D89FA8261BE41EDEB1" }, - { name = "gleam_community_ansi", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_community_colour", "gleam_stdlib"], otp_app = "gleam_community_ansi", source = "hex", outer_checksum = "FE79E08BF97009729259B6357EC058315B6FBB916FAD1C2FF9355115FEB0D3A4" }, - { name = "gleam_community_colour", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_community_colour", source = "hex", outer_checksum = "A49A5E3AE8B637A5ACBA80ECB9B1AFE89FD3D5351FF6410A42B84F666D40D7D5" }, - { name = "gleam_community_maths", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_community_maths", source = "hex", outer_checksum = "E30C61A75051DAF7CFD77C4FBAA04140FDA0B5D831955E7A74521E5576E2780D" }, - { name = "gleam_erlang", version = "0.23.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "C21CFB816C114784E669FFF4BBF433535EEA9960FA2F216209B8691E87156B96" }, - { name = "gleam_http", version = "3.6.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_http", source = "hex", outer_checksum = "8C07DF9DF8CC7F054C650839A51C30A7D3C26482AC241C899C1CEA86B22DBE51" }, - { name = "gleam_httpc", version = "2.1.2", build_tools = ["gleam"], requirements = ["gleam_http", "gleam_stdlib"], otp_app = "gleam_httpc", source = "hex", outer_checksum = "ACD05CA3BAC7780DF5FFAE334621FD199D1B490FAF6ECDFF74316CAA61CE88E6" }, - { name = "gleam_otp", version = "0.8.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "18EF8242A5E54BA92F717C7222F03B3228AEE00D1F286D4C56C3E8C18AA2588E" }, - { name = "gleam_stdlib", version = "0.36.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "C0D14D807FEC6F8A08A7C9EF8DFDE6AE5C10E40E21325B2B29365965D82EB3D4" }, - { name = "glint", version = "0.13.0", build_tools = ["gleam"], requirements = ["gleam_community_ansi", "gleam_community_colour", "gleam_stdlib", "snag"], otp_app = "glint", source = "hex", outer_checksum = "46E56049CD370D61F720D319D0AB970408C9336EEB918F08B5DCB1DCE9845FA3" }, - { name = "pqueue", version = "2.0.7", build_tools = ["rebar3"], requirements = [], otp_app = "pqueue", source = "hex", outer_checksum = "8B0204BB202335890E4E7F9B99A8EC0B84DDB8513EE298EB180EE9B3BCB4C859" }, - { name = "simplifile", version = "1.5.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "EB9AA8E65E5C1E3E0FDCFC81BC363FD433CB122D7D062750FFDF24DE4AC40116" }, - { name = "snag", version = "0.2.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "snag", source = "hex", outer_checksum = "8FD70D8FB3728E08AC425283BB509BB0F012BE1AE218424A597CDE001B0EE589" }, - { name = "tom", version = "0.2.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "tom", source = "hex", outer_checksum = "5C5A9B8586C547F1F39542B1A3BBD9FEE17AFEAB51CE53B32B13D0D46B421249" }, -] - -[requirements] -adglent = { version = "~> 1.2" } -gleam_community_maths = { version = "~> 1.0" } -gleam_erlang = { version = "~> 0.23" } -gleam_otp = { version = "~> 0.8" } -gleam_stdlib = { version = "~> 0.33" } -pqueue = { version = "~> 2.0" } -simplifile = { version = "~> 1.0" } diff --git a/aoc2023-gleam/src/.gitignore b/aoc2023-gleam/src/.gitignore deleted file mode 100644 index bc13a69..0000000 --- a/aoc2023-gleam/src/.gitignore +++ /dev/null @@ -1 +0,0 @@ -aoc2023.gleam \ No newline at end of file diff --git a/aoc2023-gleam/src/day1/.gitignore b/aoc2023-gleam/src/day1/.gitignore deleted file mode 100644 index ae40cea..0000000 --- a/aoc2023-gleam/src/day1/.gitignore +++ /dev/null @@ -1 +0,0 @@ -input.txt \ No newline at end of file diff --git a/aoc2023-gleam/src/day1/solve.gleam b/aoc2023-gleam/src/day1/solve.gleam deleted file mode 100644 index 37a19d2..0000000 --- a/aoc2023-gleam/src/day1/solve.gleam +++ /dev/null @@ -1,57 +0,0 @@ -import adglent.{First, Second} -import gleam/io -import gleam/list -import gleam/string -import gleam/regex.{type Match, Match} -import gleam/int - -pub fn part1(input: String) { - let assert Ok(re) = regex.from_string("[1-9]") - - input - |> string.split("\n") - |> list.fold(0, fn(acc, s) { - let matches = regex.scan(s, with: re) - - let assert Ok(Match(content: first, ..)) = list.first(matches) - let assert Ok(Match(content: last, ..)) = list.last(matches) - let assert Ok(i) = int.parse(first <> last) - acc + i - }) - |> string.inspect -} - -const substitutions = [ - #("one", "o1e"), - #("two", "t2o"), - #("three", "t3e"), - #("four", "4"), - #("five", "5e"), - #("six", "6"), - #("seven", "7n"), - #("eight", "e8t"), - #("nine", "n9e"), -] - -pub fn part2(input: String) { - list.fold(over: substitutions, from: input, with: fn(acc, sub) { - let #(from, to) = sub - string.replace(in: acc, each: from, with: to) - }) - |> part1 -} - -pub fn main() { - let assert Ok(part) = adglent.get_part() - let assert Ok(input) = adglent.get_input("1") - case part { - First -> - part1(input) - |> adglent.inspect - |> io.println - Second -> - part2(input) - |> adglent.inspect - |> io.println - } -} diff --git a/aoc2023-gleam/src/day10/.gitignore b/aoc2023-gleam/src/day10/.gitignore deleted file mode 100644 index ae40cea..0000000 --- a/aoc2023-gleam/src/day10/.gitignore +++ /dev/null @@ -1 +0,0 @@ -input.txt \ No newline at end of file diff --git a/aoc2023-gleam/src/day10/solve.gleam b/aoc2023-gleam/src/day10/solve.gleam deleted file mode 100644 index c33634d..0000000 --- a/aoc2023-gleam/src/day10/solve.gleam +++ /dev/null @@ -1,177 +0,0 @@ -import adglent.{First, Second} -import gleam/bool -import gleam/dict.{type Dict} -import gleam/list -import gleam/int -import gleam/io -import gleam/set.{type Set} -import gleam/string - -type Posn { - Posn(row: Int, col: Int) -} - -fn add_posns(p1: Posn, p2: Posn) -> Posn { - Posn(p1.row + p2.row, p1.col + p2.col) -} - -type PipeGrid = - Dict(Posn, String) - -const north = Posn(-1, 0) - -const south = Posn(1, 0) - -const east = Posn(0, 1) - -const west = Posn(0, -1) - -const initial_directions = [ - #(north, ["|", "7", "F"]), - #(south, ["|", "J", "L"]), - #(east, ["-", "J", "7"]), - #(west, ["-", "F", "L"]), -] - -fn pipe_neighbors(pipe: String) -> List(Posn) { - case pipe { - "|" -> [north, south] - "-" -> [east, west] - "L" -> [north, east] - "F" -> [south, east] - "7" -> [south, west] - "J" -> [north, west] - _ -> panic as "bad pipe" - } -} - -fn make_grid(input: String) -> PipeGrid { - { - use r, row <- list.index_map(string.split(input, "\n")) - use c, col <- list.index_map(string.to_graphemes(row)) - #(Posn(r, c), col) - } - |> list.flatten - |> dict.from_list -} - -fn valid_start_direction(grid: PipeGrid, s: Posn) { - let assert [dir, ..] = { - use d <- list.filter_map(initial_directions) - let #(delta, valids) = d - let neighbor = add_posns(s, delta) - case dict.get(grid, neighbor) { - Ok(pipe) -> - case list.contains(valids, pipe) { - True -> Ok(neighbor) - False -> Error(Nil) - } - Error(_) -> Error(Nil) - } - } - dir -} - -fn to_next_pipe(current: Posn, grid: PipeGrid, acc: List(Posn)) { - let assert [prev, ..] = acc - let assert Ok(pipe) = dict.get(grid, current) - use <- bool.guard(pipe == "S", [current, ..acc]) - let assert [next] = { - pipe - |> pipe_neighbors - |> list.filter_map(fn(p) { - case add_posns(p, current) { - neighbor if neighbor == prev -> Error(Nil) - neighbor -> Ok(neighbor) - } - }) - } - to_next_pipe(next, grid, [current, ..acc]) -} - -pub fn part1(input: String) { - let grid = - input - |> make_grid - - let assert Ok(s) = - grid - |> dict.filter(fn(_, v) { v == "S" }) - |> dict.keys - |> list.first - - grid - |> valid_start_direction(s) - |> to_next_pipe(grid, [s]) - |> list.length - |> fn(i) { { { i - 1 } / 2 } } - |> string.inspect -} - -fn trace_ray(p: Posn, loop: Set(Posn), grid: PipeGrid) -> Bool { - use <- bool.guard(set.contains(loop, p), False) - int.is_odd(count_crossings(p, loop, grid, 0, "")) -} - -fn count_crossings( - p: Posn, - loop: Set(Posn), - grid: PipeGrid, - acc: Int, - corner: String, -) { - let maybe_cell = dict.get(grid, p) - use <- bool.guard(maybe_cell == Error(Nil), acc) - let assert Ok(cell) = maybe_cell - let next = add_posns(p, east) - case set.contains(loop, p) { - False -> count_crossings(next, loop, grid, acc, corner) - True -> - case corner, cell { - _, "|" -> count_crossings(next, loop, grid, acc + 1, corner) - _, "F" | _, "L" -> count_crossings(next, loop, grid, acc, cell) - "F", "J" | "L", "7" -> count_crossings(next, loop, grid, acc + 1, "") - "F", "7" | "L", "J" -> count_crossings(next, loop, grid, acc, "") - _, _ -> count_crossings(next, loop, grid, acc, corner) - } - } -} - -pub fn part2(input: String) { - let grid = - input - |> make_grid - - let assert Ok(s) = - grid - |> dict.filter(fn(_, v) { v == "S" }) - |> dict.keys - |> list.first - - let loop_pipes = - grid - |> valid_start_direction(s) - |> to_next_pipe(grid, [s]) - |> set.from_list - - grid - |> dict.keys - |> list.filter(trace_ray(_, loop_pipes, grid)) - |> list.length() - |> string.inspect -} - -pub fn main() { - let assert Ok(part) = adglent.get_part() - let assert Ok(input) = adglent.get_input("10") - case part { - First -> - part1(input) - |> adglent.inspect - |> io.println - Second -> - part2(input) - |> adglent.inspect - |> io.println - } -} diff --git a/aoc2023-gleam/src/day11/.gitignore b/aoc2023-gleam/src/day11/.gitignore deleted file mode 100644 index ae40cea..0000000 --- a/aoc2023-gleam/src/day11/.gitignore +++ /dev/null @@ -1 +0,0 @@ -input.txt \ No newline at end of file diff --git a/aoc2023-gleam/src/day11/solve.gleam b/aoc2023-gleam/src/day11/solve.gleam deleted file mode 100644 index 35464a1..0000000 --- a/aoc2023-gleam/src/day11/solve.gleam +++ /dev/null @@ -1,84 +0,0 @@ -import adglent.{First, Second} -import gleam/io -import gleam/int -import gleam/string -import gleam/list - -type Posn { - Posn(x: Int, y: Int) -} - -fn find_empty(grid: List(List(String))) { - use acc, row, r <- list.index_fold(grid, []) - case list.unique(row) { - ["."] -> [r, ..acc] - _ -> acc - } -} - -fn count_prior_empty_ranks(rank: Int, empty_ranks: List(Int)) -> Int { - empty_ranks - |> list.drop_while(fn(r_empty) { r_empty > rank }) - |> list.length -} - -fn parse_with_expansion(input: String, expansion: Int) -> List(Posn) { - let add = expansion - 1 - let grid = - input - |> string.split("\n") - |> list.map(string.to_graphemes) - - let empty_row_list = find_empty(grid) - let empty_col_list = find_empty(list.transpose(grid)) - - { - use r, row <- list.index_map(grid) - use acc, cell, c <- list.index_fold(over: row, from: []) - - let p = Posn(r, c) - let empty_r = count_prior_empty_ranks(r, empty_row_list) - let empty_c = count_prior_empty_ranks(c, empty_col_list) - case cell { - "#" -> [Posn(p.x + empty_r * add, p.y + empty_c * add), ..acc] - _empty -> acc - } - } - |> list.flatten() -} - -fn all_distances(stars: List(Posn)) -> Int { - use acc, pair <- list.fold(list.combination_pairs(stars), 0) - let #(s1, s2) = pair - acc + int.absolute_value(s1.x - s2.x) + int.absolute_value(s1.y - s2.y) -} - -fn find_distances(input: String, expand_by: Int) -> String { - input - |> parse_with_expansion(expand_by) - |> all_distances - |> string.inspect -} - -pub fn part1(input: String) { - find_distances(input, 2) -} - -pub fn part2(input: String) { - find_distances(input, 1_000_000) -} - -pub fn main() { - let assert Ok(part) = adglent.get_part() - let assert Ok(input) = adglent.get_input("11") - case part { - First -> - part1(input) - |> adglent.inspect - |> io.println - Second -> - part2(input) - |> adglent.inspect - |> io.println - } -} diff --git a/aoc2023-gleam/src/day12/.gitignore b/aoc2023-gleam/src/day12/.gitignore deleted file mode 100644 index ae40cea..0000000 --- a/aoc2023-gleam/src/day12/.gitignore +++ /dev/null @@ -1 +0,0 @@ -input.txt \ No newline at end of file diff --git a/aoc2023-gleam/src/day12/solve.gleam b/aoc2023-gleam/src/day12/solve.gleam deleted file mode 100644 index 893b83c..0000000 --- a/aoc2023-gleam/src/day12/solve.gleam +++ /dev/null @@ -1,90 +0,0 @@ -import adglent.{First, Second} -import gleam/io -import gleam/string -import gleam/list -import gleam/int -import gleam/result -import utilities/memo.{type Cache} - -type ParserState = - #(String, List(Int), Int, Bool) - -fn parse_folds(input: String, folds: Int) { - let records = string.split(input, "\n") - use record <- list.map(records) - let assert Ok(#(template, sets_str)) = string.split_once(record, " ") - - let template = - template - |> list.repeat(folds) - |> list.intersperse("?") - |> string.concat - let sets = - sets_str - |> string.split(",") - |> list.map(int.parse) - |> result.values - |> list.repeat(folds) - |> list.flatten() - - #(template, sets) -} - -fn do_count( - template: String, - groups: List(Int), - left: Int, - gap: Bool, - cache: Cache(ParserState, Int), -) -> Int { - use <- memo.memoize(cache, #(template, groups, left, gap)) - case template, groups, left, gap { - "", [], 0, _ -> 1 - "?" <> t_rest, [g, ..g_rest], 0, False -> - do_count(t_rest, g_rest, g - 1, g == 1, cache) - + do_count(t_rest, groups, 0, False, cache) - "?" <> t_rest, [], 0, False - | "?" <> t_rest, _, 0, True - | "." <> t_rest, _, 0, _ -> do_count(t_rest, groups, 0, False, cache) - "#" <> t_rest, [g, ..g_rest], 0, False -> - do_count(t_rest, g_rest, g - 1, g == 1, cache) - "?" <> t_rest, gs, l, False | "#" <> t_rest, gs, l, False -> - do_count(t_rest, gs, l - 1, l == 1, cache) - _, _, _, _ -> 0 - } -} - -fn count_solutions(acc: Int, condition: #(String, List(Int))) -> Int { - use cache: Cache(ParserState, Int) <- memo.create() - let #(template, groups) = condition - acc + do_count(template, groups, 0, False, cache) -} - -pub fn part1(input: String) { - input - |> parse_folds(1) - |> list.fold(0, count_solutions) - |> string.inspect -} - -pub fn part2(input: String) { - input - |> parse_folds(5) - |> list.fold(0, count_solutions) - |> string.inspect -} - -pub fn main() { - let assert Ok(part) = adglent.get_part() - let assert Ok(input) = adglent.get_input("12") - case part { - First -> - part1(input) - |> adglent.inspect - |> io.println - Second -> - part2(input) - |> adglent.inspect - |> io.println - } -} diff --git a/aoc2023-gleam/src/day13/.gitignore b/aoc2023-gleam/src/day13/.gitignore deleted file mode 100644 index ae40cea..0000000 --- a/aoc2023-gleam/src/day13/.gitignore +++ /dev/null @@ -1 +0,0 @@ -input.txt \ No newline at end of file diff --git a/aoc2023-gleam/src/day13/solve.gleam b/aoc2023-gleam/src/day13/solve.gleam deleted file mode 100644 index 6f9b9a0..0000000 --- a/aoc2023-gleam/src/day13/solve.gleam +++ /dev/null @@ -1,87 +0,0 @@ -import adglent.{First, Second} -import gleam/io -import gleam/list -import gleam/string -import gleam/bool - -type SymmetryType { - Horizontal(Int) - Vertical(Int) -} - -fn is_symmetric(xss: List(List(a)), errs: Int) { - let assert [left, ..right] = xss - do_is_symmetric([left], right, errs) -} - -fn do_is_symmetric( - left: List(List(a)), - right: List(List(a)), - errors: Int, -) -> Result(Int, Nil) { - use <- bool.guard(list.is_empty(right), Error(Nil)) - let assert [h, ..t] = right - let found_errors = - list.zip(list.flatten(left), list.flatten(right)) - |> list.filter(fn(tup) { tup.1 != tup.0 }) - |> list.length - case found_errors == errors { - True -> Ok(list.length(left)) - False -> do_is_symmetric([h, ..left], t, errors) - } -} - -fn get_symmetry_type(xss: List(List(String)), errors: Int) { - case is_symmetric(xss, errors) { - Ok(n) -> Horizontal(n) - _ -> { - let assert Ok(n) = is_symmetric(list.transpose(xss), errors) - Vertical(n) - } - } -} - -fn summarize_notes(symmetries: List(SymmetryType)) { - use acc, note <- list.fold(symmetries, 0) - acc - + case note { - Horizontal(n) -> 100 * n - Vertical(n) -> n - } -} - -fn solve(input: String, errors: Int) { - input - |> string.split("\n\n") - |> list.map(fn(strs) { - strs - |> string.split("\n") - |> list.map(string.to_graphemes) - |> get_symmetry_type(errors) - }) - |> summarize_notes - |> string.inspect -} - -pub fn part1(input: String) { - solve(input, 0) -} - -pub fn part2(input: String) { - solve(input, 1) -} - -pub fn main() { - let assert Ok(part) = adglent.get_part() - let assert Ok(input) = adglent.get_input("13") - case part { - First -> - part1(input) - |> adglent.inspect - |> io.println - Second -> - part2(input) - |> adglent.inspect - |> io.println - } -} diff --git a/aoc2023-gleam/src/day14/.gitignore b/aoc2023-gleam/src/day14/.gitignore deleted file mode 100644 index ae40cea..0000000 --- a/aoc2023-gleam/src/day14/.gitignore +++ /dev/null @@ -1 +0,0 @@ -input.txt \ No newline at end of file diff --git a/aoc2023-gleam/src/day14/solve.gleam b/aoc2023-gleam/src/day14/solve.gleam deleted file mode 100644 index ecc5361..0000000 --- a/aoc2023-gleam/src/day14/solve.gleam +++ /dev/null @@ -1,94 +0,0 @@ -import adglent.{First, Second} -import gleam/dict -import gleam/int -import gleam/io -import gleam/list -import gleam/order -import gleam/string - -fn parse(input) { - input - |> string.split("\n") - |> list.map(string.to_graphemes) - |> list.transpose() -} - -fn roll_boulders(strs: List(String)) { - { - use chunks <- list.map(list.chunk(strs, fn(c) { c == "O" || c == "." })) - list.sort(chunks, order.reverse(string.compare)) - } - |> list.flatten -} - -fn score(matrix) { - use acc, col <- list.fold(matrix, 0) - acc - + { - use col_acc, char, n <- list.index_fold(list.reverse(col), 0) - case char { - "O" -> col_acc + n + 1 - _ -> col_acc - } - } -} - -pub fn part1(input: String) { - input - |> parse - |> list.map(roll_boulders) - |> score() - |> string.inspect -} - -fn rotate(matrix) { - matrix - |> list.map(list.reverse) - |> list.transpose -} - -fn spin(matrix) { - use acc, _ <- list.fold(list.range(1, 4), matrix) - acc - |> list.map(roll_boulders) - |> rotate -} - -fn spin_cycle(matrix) { - let cache = dict.new() - check_if_seen(matrix, cache, 1_000_000_000) -} - -fn check_if_seen(matrix, cache, count) { - case dict.get(cache, matrix) { - Error(Nil) -> - check_if_seen(spin(matrix), dict.insert(cache, matrix, count), count - 1) - Ok(n) -> { - let assert Ok(extra) = int.modulo(count, n - count) - list.fold(list.range(1, extra), matrix, fn(acc, _) { spin(acc) }) - |> score - } - } -} - -pub fn part2(input: String) { - input - |> parse - |> spin_cycle - |> string.inspect -} - -pub fn main() { - let assert Ok(part) = adglent.get_part() - let assert Ok(input) = adglent.get_input("14") - case part { - First -> - part1(input) - |> adglent.inspect - |> io.println - Second -> - part2(input) - |> adglent.inspect - |> io.println - } -} diff --git a/aoc2023-gleam/src/day15/.gitignore b/aoc2023-gleam/src/day15/.gitignore deleted file mode 100644 index ae40cea..0000000 --- a/aoc2023-gleam/src/day15/.gitignore +++ /dev/null @@ -1 +0,0 @@ -input.txt \ No newline at end of file diff --git a/aoc2023-gleam/src/day15/solve.gleam b/aoc2023-gleam/src/day15/solve.gleam deleted file mode 100644 index a7d250c..0000000 --- a/aoc2023-gleam/src/day15/solve.gleam +++ /dev/null @@ -1,104 +0,0 @@ -import adglent.{First, Second} -import gleam/io -import gleam/string -import gleam/list -import gleam/int -import gleam/dict.{type Dict} -import gleam/option.{None, Some} - -fn split(input: String) -> List(String) { - input - |> string.split(",") -} - -fn hash_algorithm(str: String) -> Int { - let codepoints = - str - |> string.to_utf_codepoints() - |> list.map(string.utf_codepoint_to_int) - use acc, c <- list.fold(codepoints, 0) - let assert Ok(acc) = int.modulo({ acc + c } * 17, 256) - acc -} - -pub fn part1(input: String) -> String { - input - |> split - |> list.fold(0, fn(acc, str) { acc + hash_algorithm(str) }) - |> string.inspect -} - -type Instruction { - Remove(label: String) - Insert(label: String, focal: Int) -} - -fn read_instruction(str: String) -> Instruction { - case string.split(str, "=") { - [label, focal_str] -> { - let assert Ok(focal) = int.parse(focal_str) - Insert(label, focal) - } - _ -> Remove(string.drop_right(str, 1)) - } -} - -fn parse_instructions(insts: List(String)) -> Dict(Int, List(#(String, Int))) { - use acc, inst <- list.fold(insts, dict.new()) - case read_instruction(inst) { - Remove(label) -> remove_lens(acc, label) - Insert(label, focal) -> insert_lens(acc, label, focal) - } -} - -fn remove_lens(boxes, label) { - use v <- dict.update(boxes, hash_algorithm(label)) - case v { - Some(lenses) -> - case list.key_pop(lenses, label) { - Ok(#(_, updated)) -> updated - Error(Nil) -> lenses - } - None -> [] - } -} - -fn insert_lens(boxes, label, focal) { - use v <- dict.update(boxes, hash_algorithm(label)) - case v { - Some(lenses) -> list.key_set(lenses, label, focal) - None -> [#(label, focal)] - } -} - -fn focusing_power(boxes: Dict(Int, List(#(String, Int)))) -> Int { - use acc, k, v <- dict.fold(boxes, 0) - let box_acc = { - use acc, lens, i <- list.index_fold(v, 0) - acc + lens.1 * { i + 1 } - } - acc + { k + 1 } * box_acc -} - -pub fn part2(input: String) -> String { - input - |> split - |> parse_instructions - |> focusing_power - |> string.inspect -} - -pub fn main() { - let assert Ok(part) = adglent.get_part() - let assert Ok(input) = adglent.get_input("15") - case part { - First -> - part1(input) - |> adglent.inspect - |> io.println - Second -> - part2(input) - |> adglent.inspect - |> io.println - } -} diff --git a/aoc2023-gleam/src/day16/.gitignore b/aoc2023-gleam/src/day16/.gitignore deleted file mode 100644 index ae40cea..0000000 --- a/aoc2023-gleam/src/day16/.gitignore +++ /dev/null @@ -1 +0,0 @@ -input.txt \ No newline at end of file diff --git a/aoc2023-gleam/src/day16/solve.gleam b/aoc2023-gleam/src/day16/solve.gleam deleted file mode 100644 index 65ce36b..0000000 --- a/aoc2023-gleam/src/day16/solve.gleam +++ /dev/null @@ -1,119 +0,0 @@ -import adglent.{First, Second} -import gleam/bool -import gleam/dict.{type Dict} -import gleam/io -import gleam/list -import gleam/result -import gleam/set.{type Set} -import utilities/array2d.{type Posn, Posn} - -type Direction { - Up - Right - Down - Left -} - -type Light { - Light(posn: Posn, dir: Direction) -} - -fn move(l: Light) -> Light { - let Light(p, dir) = l - case dir { - Up -> Light(..l, posn: Posn(..p, r: p.r - 1)) - Down -> Light(..l, posn: Posn(..p, r: p.r + 1)) - Left -> Light(..l, posn: Posn(..p, c: p.c - 1)) - Right -> Light(..l, posn: Posn(..p, c: p.c + 1)) - } -} - -fn transform(l: Light, cell: Result(String, Nil)) -> List(Light) { - use <- bool.guard(result.is_error(cell), []) - let assert Ok(c) = cell - let Light(p, dir) = l - case dir, c { - // no change - _, "." | Up, "|" | Down, "|" | Left, "-" | Right, "-" -> [l] - // diagonal mirrors - Left, "/" -> [Light(p, Down)] - Down, "/" -> [Light(p, Left)] - Right, "/" -> [Light(p, Up)] - Up, "/" -> [Light(p, Right)] - Left, "\\" -> [Light(p, Up)] - Up, "\\" -> [Light(p, Left)] - Right, "\\" -> [Light(p, Down)] - Down, "\\" -> [Light(p, Right)] - // splitters - Left, "|" | Right, "|" -> [Light(p, Up), Light(p, Down)] - Up, "-" | Down, "-" -> [Light(p, Left), Light(p, Right)] - _, _ -> panic as "unrecognized cell type" - } -} - -fn energize(lights: List(Light), visited: Set(Light), grid: Dict(Posn, String)) { - let next_positions = - lights - |> list.flat_map(fn(l) { - let next = move(l) - transform(next, dict.get(grid, next.posn)) - }) - |> list.filter(fn(l) { !set.contains(visited, l) }) - let all_visited = set.union(set.from_list(next_positions), visited) - case visited == all_visited { - True -> - set.fold(visited, set.new(), fn(acc, l) { set.insert(acc, l.posn) }) - |> set.to_list - |> list.length - False -> energize(next_positions, all_visited, grid) - } -} - -pub fn part1(input: String) { - let grid = array2d.parse_grid(input) - - [Light(Posn(0, -1), Right)] - |> energize(set.new(), grid) -} - -pub fn part2(input: String) { - let grid = array2d.parse_grid(input) - - let Posn(rows, cols) = { - use acc, p <- list.fold(dict.keys(grid), Posn(0, 0)) - case acc.r + acc.c > p.r + p.c { - True -> acc - False -> p - } - } - - let all_starts = - list.concat([ - list.map(list.range(0, rows), fn(r) { Light(Posn(r, -1), Right) }), - list.map(list.range(0, rows), fn(r) { Light(Posn(r, cols + 1), Left) }), - list.map(list.range(0, cols), fn(c) { Light(Posn(-1, c), Down) }), - list.map(list.range(0, cols), fn(c) { Light(Posn(rows + 1, c), Up) }), - ]) - - use acc, p <- list.fold(all_starts, 0) - let energized = energize([p], set.new(), grid) - case acc > energized { - True -> acc - False -> energized - } -} - -pub fn main() { - let assert Ok(part) = adglent.get_part() - let assert Ok(input) = adglent.get_input("16") - case part { - First -> - part1(input) - |> adglent.inspect - |> io.println - Second -> - part2(input) - |> adglent.inspect - |> io.println - } -} diff --git a/aoc2023-gleam/src/day17/.gitignore b/aoc2023-gleam/src/day17/.gitignore deleted file mode 100644 index ae40cea..0000000 --- a/aoc2023-gleam/src/day17/.gitignore +++ /dev/null @@ -1 +0,0 @@ -input.txt \ No newline at end of file diff --git a/aoc2023-gleam/src/day17/solve.gleam b/aoc2023-gleam/src/day17/solve.gleam deleted file mode 100644 index 7a01c4d..0000000 --- a/aoc2023-gleam/src/day17/solve.gleam +++ /dev/null @@ -1,143 +0,0 @@ -import adglent.{First, Second} -import gleam/bool -import gleam/dict.{type Dict} -import gleam/io -import gleam/list -import gleam/result -import gleam/string -import gleam/set.{type Set} -import utilities/array2d.{type Posn, Posn} -import utilities/prioqueue.{type PriorityQueue} - -type State { - State(posn: Posn, heatloss: Int, previous: Posn, history: List(Posn)) -} - -const deltas = [Posn(-1, 0), Posn(1, 0), Posn(0, -1), Posn(0, 1)] - -fn make_key(s: State) { - #(s.posn, same_dir(s)) -} - -fn same_dir(s: State) { - case s.history { - [] -> [] - [first, ..] as deltas -> - list.take_while(deltas, fn(d) { d == first }) - |> list.take(10) - } -} - -fn is_goal(s: State, min_run: Int, goal: Posn) { - goal == s.posn && list.length(same_dir(s)) >= min_run -} - -fn find_good_neighbors(max: Int, min: Int, s: State, grid) { - deltas - |> list.filter(eliminate_bad_neighbors(_, s, max, min, grid)) - |> list.map(make_state(_, s, grid)) -} - -fn eliminate_bad_neighbors(d: Posn, s: State, max, min, grid) { - let neighbor = array2d.add_posns(d, s.posn) - - use <- bool.guard( - neighbor == s.previous || !dict.has_key(grid, neighbor), - False, - ) - case same_dir(s), list.length(same_dir(s)) { - [prev, ..], l if l == max -> d != prev - _, 0 -> True - [prev, ..], l if l < min -> d == prev - _, _ -> True - } -} - -fn make_state(d: Posn, s: State, grid) { - let neighbor = array2d.add_posns(d, s.posn) - let assert Ok(heat_lost) = dict.get(grid, neighbor) - State( - posn: neighbor, - heatloss: s.heatloss + heat_lost, - previous: s.posn, - history: [d, ..s.history], - ) -} - -fn find_path( - grid: Dict(Posn, Int), - queue: PriorityQueue(State), - seen: Set(#(Posn, List(Posn))), - get_neighbors: fn(State) -> List(State), - is_goal: fn(State) -> Bool, -) { - let assert Ok(#(state, rest)) = prioqueue.pop(queue) - let key = - make_key( - state - |> io.debug, - ) - case set.contains(seen, key) { - True -> find_path(grid, rest, seen, get_neighbors, is_goal) - False -> { - let now_seen = set.insert(seen, key) - let neighbors = get_neighbors(state) - case list.find(neighbors, is_goal) { - Ok(final) -> final.heatloss - _err -> { - let now_queue = - list.fold(neighbors, rest, fn(acc, n) { - prioqueue.insert(acc, n, n.heatloss) - }) - find_path(grid, now_queue, now_seen, get_neighbors, is_goal) - } - } - } - } -} - -pub fn part1(input: String) { - let raw_grid = - input - |> array2d.to_list_of_lists - - let grid = array2d.to_2d_intarray(raw_grid) - - let rmax = list.length(raw_grid) - let assert Ok(cmax) = - raw_grid - |> list.first - |> result.map(list.length) - - let start = State(Posn(0, 0), 0, Posn(0, 0), []) - let goal = Posn(rmax, cmax) - - find_path( - grid, - prioqueue.insert(prioqueue.new(), start, 0), - set.new(), - find_good_neighbors(0, 3, _, grid), - is_goal(_, 1, goal), - ) - |> string.inspect -} - -pub fn part2(input: String) { - input - |> string.inspect -} - -pub fn main() { - let assert Ok(part) = adglent.get_part() - let assert Ok(input) = adglent.get_input("17") - case part { - First -> - part1(input) - |> adglent.inspect - |> io.println - Second -> - part2(input) - |> adglent.inspect - |> io.println - } -} diff --git a/aoc2023-gleam/src/day18/.gitignore b/aoc2023-gleam/src/day18/.gitignore deleted file mode 100644 index ae40cea..0000000 --- a/aoc2023-gleam/src/day18/.gitignore +++ /dev/null @@ -1 +0,0 @@ -input.txt \ No newline at end of file diff --git a/aoc2023-gleam/src/day18/solve.gleam b/aoc2023-gleam/src/day18/solve.gleam deleted file mode 100644 index 2c000f9..0000000 --- a/aoc2023-gleam/src/day18/solve.gleam +++ /dev/null @@ -1,113 +0,0 @@ -import adglent.{First, Second} -import gleam/io -import gleam/int -import gleam/list -import gleam/option.{Some} -import gleam/regex.{type Match, Match} -import gleam/string - -type Coord { - Coord(x: Int, y: Int) -} - -type Direction { - Up - Right - Down - Left -} - -type Dig { - Dig(dir: Direction, dist: Int) -} - -fn to_direction(c: String) { - case c { - "R" | "0" -> Right - "D" | "1" -> Down - "L" | "2" -> Left - "U" | "3" -> Up - _ -> panic - } -} - -fn parse_front(line: String) { - let assert Ok(re) = regex.from_string("(.) (.*) \\(.*\\)") - let assert [Match(submatches: [Some(dir), Some(dist)], ..)] = - regex.scan(with: re, content: line) - let assert Ok(n) = int.parse(dist) - Dig(to_direction(dir), n) -} - -fn parse_hex(line: String) { - let assert Ok(re) = regex.from_string("\\(#(.....)(.)\\)") - let assert [Match(submatches: [Some(dist), Some(dir)], ..)] = - regex.scan(with: re, content: line) - let assert Ok(n) = int.base_parse(dist, 16) - Dig(to_direction(dir), n) -} - -fn go(current: Coord, dig: Dig) { - case dig { - Dig(Up, n) -> Coord(current.x, current.y + n) - Dig(Right, n) -> Coord(current.x + n, current.y) - Dig(Down, n) -> Coord(current.x, current.y - n) - Dig(Left, n) -> Coord(current.x - n, current.y) - } -} - -fn double_triangle(c1: Coord, c2: Coord) { - { c1.x * c2.y } - { c2.x * c1.y } -} - -fn start_dig(digs: List(Dig)) { - do_next_dig(digs, Coord(0, 0), 0, 0) -} - -fn do_next_dig( - digs: List(Dig), - current: Coord, - area: Int, - perimeter: Int, -) -> Int { - case digs { - [] -> int.absolute_value(area) / 2 + { perimeter / 2 } + 1 - [dig, ..rest] -> { - let next = go(current, dig) - let area = area + double_triangle(current, next) - let perimeter = perimeter + dig.dist - do_next_dig(rest, next, area, perimeter) - } - } -} - -fn solve_with(input, f) { - input - |> string.split("\n") - |> list.map(f) - |> start_dig - |> string.inspect -} - -pub fn part1(input: String) { - solve_with(input, parse_front) -} - -pub fn part2(input: String) { - solve_with(input, parse_hex) -} - -pub fn main() { - let assert Ok(part) = adglent.get_part() - let assert Ok(input) = adglent.get_input("18") - case part { - First -> - part1(input) - |> adglent.inspect - |> io.println - Second -> - part2(input) - |> adglent.inspect - |> io.println - } -} diff --git a/aoc2023-gleam/src/day19/.gitignore b/aoc2023-gleam/src/day19/.gitignore deleted file mode 100644 index ae40cea..0000000 --- a/aoc2023-gleam/src/day19/.gitignore +++ /dev/null @@ -1 +0,0 @@ -input.txt \ No newline at end of file diff --git a/aoc2023-gleam/src/day19/solve.gleam b/aoc2023-gleam/src/day19/solve.gleam deleted file mode 100644 index 186e783..0000000 --- a/aoc2023-gleam/src/day19/solve.gleam +++ /dev/null @@ -1,255 +0,0 @@ -import adglent.{First, Second} -import gleam/io -import gleam/string -import gleam/dict.{type Dict} -import gleam/order.{type Order, Gt, Lt} -import gleam/regex.{type Match, Match} -import gleam/list -import gleam/option.{Some} -import gleam/int - -type Rating { - XtremelyCool - Musical - Aerodynamic - Shiny -} - -type Part { - Part(x: Int, m: Int, a: Int, s: Int) -} - -type Action { - Accept - Reject - SendTo(String) -} - -type Rule { - If(rating: Rating, comparison: Order, threshold: Int, do: Action) - Just(do: Action) -} - -type Workflow = - Dict(String, List(Rule)) - -type Interval { - Interval(min: Int, max: Int) -} - -type PartRange { - PartRange(x: Interval, m: Interval, a: Interval, s: Interval) -} - -fn parse_workflow(input: String) -> Workflow { - let assert Ok(re) = regex.from_string("(.*){(.*)}") - - use acc, line <- list.fold(string.split(input, "\n"), dict.new()) - let assert [Match(submatches: [Some(name), Some(all_rules)], ..)] = - regex.scan(re, line) - let rules = - string.split(all_rules, ",") - |> parse_rules - dict.insert(acc, name, rules) -} - -fn parse_rules(rules: List(String)) -> List(Rule) { - let assert Ok(re_rule) = regex.from_string("(.*)(>|<)(.*):(.*)") - use rule <- list.map(rules) - case regex.scan(re_rule, rule) { - [Match(submatches: [Some(r), Some(c), Some(t), Some(i)], ..)] -> - If(to_rating(r), to_comp(c), to_val(t), to_instruction(i)) - _nomatch -> Just(to_instruction(rule)) - } -} - -fn to_instruction(rule: String) { - case rule { - "A" -> Accept - "R" -> Reject - name -> SendTo(name) - } -} - -fn to_rating(rating: String) { - case rating { - "x" -> XtremelyCool - "m" -> Musical - "a" -> Aerodynamic - _s -> Shiny - } -} - -fn get_rating(part: Part, rating: Rating) -> Int { - case rating { - XtremelyCool -> part.x - Musical -> part.m - Aerodynamic -> part.a - Shiny -> part.s - } -} - -fn to_comp(comp: String) { - case comp { - "<" -> Lt - _gt -> Gt - } -} - -fn to_val(val: String) { - let assert Ok(n) = int.parse(val) - n -} - -fn parse_parts(input: String) -> List(Part) { - let assert Ok(re) = regex.from_string("{x=(.*),m=(.*),a=(.*),s=(.*)}") - - use part <- list.map(string.split(input, "\n")) - let assert [Match(submatches: [Some(x), Some(m), Some(a), Some(s)], ..)] = - regex.scan(re, part) - Part(to_val(x), to_val(m), to_val(a), to_val(s)) -} - -fn start_evaluating_workflow(part: Part, workflow: Workflow) -> Int { - evaluate_workflow(part, "in", workflow) -} - -fn evaluate_workflow(part: Part, name: String, workflow: Workflow) -> Int { - let assert Ok(rules) = dict.get(workflow, name) - case evaluate_rules(part, rules) { - Accept -> part.x + part.m + part.a + part.s - Reject -> 0 - SendTo(name) -> evaluate_workflow(part, name, workflow) - } -} - -fn evaluate_rules(part: Part, rules: List(Rule)) -> Action { - case rules { - [] -> panic - [Just(do), ..] -> do - [If(rating, comparison, threshold, do), ..rest] -> - case int.compare(get_rating(part, rating), threshold) == comparison { - True -> do - False -> evaluate_rules(part, rest) - } - } -} - -pub fn part1(input: String) { - let assert Ok(#(workflows_str, parts_str)) = string.split_once(input, "\n\n") - - let workflows = parse_workflow(workflows_str) - let parts = parse_parts(parts_str) - - list.map(parts, start_evaluating_workflow(_, workflows)) - |> int.sum - |> string.inspect -} - -fn size(interval: Interval) { - interval.max - interval.min + 1 -} - -fn all_in_range(pr: PartRange) { - size(pr.x) * size(pr.m) * size(pr.a) * size(pr.s) -} - -fn get_partrange(pr: PartRange, rating: Rating) -> Interval { - case rating { - XtremelyCool -> pr.x - Musical -> pr.m - Aerodynamic -> pr.a - Shiny -> pr.s - } -} - -fn update_partrange(pr: PartRange, rating: Rating, i: Interval) -> PartRange { - case rating { - XtremelyCool -> PartRange(..pr, x: i) - Musical -> PartRange(..pr, m: i) - Aerodynamic -> PartRange(..pr, a: i) - Shiny -> PartRange(..pr, s: i) - } -} - -pub fn part2(input: String) { - let assert Ok(#(workflows_str, _)) = string.split_once(input, "\n\n") - - let workflow = parse_workflow(workflows_str) - let start = Interval(1, 4000) - - PartRange(start, start, start, start) - |> evaluate_workflow_on_range("in", workflow) - |> string.inspect -} - -fn evaluate_workflow_on_range( - pr: PartRange, - name: String, - workflow: Workflow, -) -> Int { - let assert Ok(rules) = dict.get(workflow, name) - evaluate_rules_on_range(pr, rules, workflow) -} - -fn evaluate_rules_on_range( - pr: PartRange, - rules: List(Rule), - workflow: Workflow, -) -> Int { - case rules { - [Just(Accept), ..] -> all_in_range(pr) - [Just(Reject), ..] -> 0 - [Just(SendTo(name)), ..] -> evaluate_workflow_on_range(pr, name, workflow) - [If(rating, comparison, t, action), ..rest] -> { - let mod_i = get_partrange(pr, rating) - case comparison { - Lt -> - split_range( - keep: update_partrange(pr, rating, Interval(mod_i.min, t - 1)), - and_do: action, - pass: update_partrange(pr, rating, Interval(t, mod_i.max)), - and_eval: rest, - with: workflow, - ) - _gt -> - split_range( - keep: update_partrange(pr, rating, Interval(t + 1, mod_i.max)), - and_do: action, - pass: update_partrange(pr, rating, Interval(mod_i.min, t)), - and_eval: rest, - with: workflow, - ) - } - } - [] -> panic - } -} - -fn split_range( - keep keep: PartRange, - and_do action: Action, - pass pass: PartRange, - and_eval rest: List(Rule), - with workflow: Workflow, -) -> Int { - int.add( - evaluate_rules_on_range(keep, [Just(action)], workflow), - evaluate_rules_on_range(pass, rest, workflow), - ) -} - -pub fn main() { - let assert Ok(part) = adglent.get_part() - let assert Ok(input) = adglent.get_input("19") - case part { - First -> - part1(input) - |> adglent.inspect - |> io.println - Second -> - part2(input) - |> adglent.inspect - |> io.println - } -} diff --git a/aoc2023-gleam/src/day2/.gitignore b/aoc2023-gleam/src/day2/.gitignore deleted file mode 100644 index ae40cea..0000000 --- a/aoc2023-gleam/src/day2/.gitignore +++ /dev/null @@ -1 +0,0 @@ -input.txt \ No newline at end of file diff --git a/aoc2023-gleam/src/day2/solve.gleam b/aoc2023-gleam/src/day2/solve.gleam deleted file mode 100644 index 608955f..0000000 --- a/aoc2023-gleam/src/day2/solve.gleam +++ /dev/null @@ -1,66 +0,0 @@ -import adglent.{First, Second} -import gleam/io -import gleam/int -import gleam/string -import gleam/list - -pub type Game { - Game(red: Int, blue: Int, green: Int) -} - -fn parse(input: String) -> List(List(Game)) { - use line <- list.map(string.split(input, "\n")) - let assert [_, rounds] = string.split(line, on: ": ") - use match <- list.map(string.split(rounds, on: "; ")) - use acc, draw <- list.fold( - over: string.split(match, on: ", "), - from: Game(0, 0, 0), - ) - let assert Ok(#(n_str, color)) = string.split_once(draw, " ") - let assert Ok(n) = int.parse(n_str) - case color { - "red" -> Game(..acc, red: n) - "blue" -> Game(..acc, blue: n) - "green" -> Game(..acc, green: n) - _ -> panic as "unrecognized color" - } -} - -pub fn part1(input: String) { - use acc, game, i <- list.index_fold(parse(input), 0) - case list.any(game, fn(m) { m.red > 12 || m.green > 13 || m.blue > 14 }) { - False -> acc + i + 1 - True -> acc - } -} - -pub fn part2(input: String) { - { - use game <- list.map(parse(input)) - use acc, match <- list.fold(game, Game(0, 0, 0)) - let Game(red: red, green: green, blue: blue) = match - Game( - red: int.max(red, acc.red), - blue: int.max(blue, acc.blue), - green: int.max(green, acc.green), - ) - } - |> list.fold(from: 0, with: fn(acc, g: Game) { - acc + g.red * g.blue * g.green - }) -} - -pub fn main() { - let assert Ok(part) = adglent.get_part() - let assert Ok(input) = adglent.get_input("2") - case part { - First -> - part1(input) - |> adglent.inspect - |> io.println - Second -> - part2(input) - |> adglent.inspect - |> io.println - } -} diff --git a/aoc2023-gleam/src/day20/.gitignore b/aoc2023-gleam/src/day20/.gitignore deleted file mode 100644 index ae40cea..0000000 --- a/aoc2023-gleam/src/day20/.gitignore +++ /dev/null @@ -1 +0,0 @@ -input.txt \ No newline at end of file diff --git a/aoc2023-gleam/src/day20/solve.gleam b/aoc2023-gleam/src/day20/solve.gleam deleted file mode 100644 index 9192dac..0000000 --- a/aoc2023-gleam/src/day20/solve.gleam +++ /dev/null @@ -1,251 +0,0 @@ -import adglent.{First, Second} -import gleam/bool -import gleam/dict.{type Dict} -import gleam/io -import gleam/iterator.{type Iterator, type Step, Next} -import gleam/list -import gleam/queue.{type Queue} -import gleam/set -import gleam/string - -type Node { - Broadcaster(children: List(String)) - Flipflop(children: List(String), state: Power) - Conjunction(children: List(String), state: Dict(String, TonePitch)) - Ground -} - -type Tone { - Tone(from: String, to: String, pitch: TonePitch) -} - -type Power { - On - Off -} - -type TonePitch { - Low - High -} - -type State { - State( - nodes: Dict(String, Node), - low: Int, - high: Int, - cycle: Int, - sentry_nodes: Dict(String, Int), - ) -} - -fn flip_power(p: Power) -> Power { - case p { - On -> Off - Off -> On - } -} - -fn flip_flop_pitch(p: Power) -> TonePitch { - case p { - Off -> High - On -> Low - } -} - -fn combinator_pitch(state) { - case list.unique(dict.values(state)) { - [High] -> Low - _ -> High - } -} - -fn get_children(node) { - case node { - Flipflop(children: cs, ..) -> cs - Conjunction(children: cs, ..) -> cs - Broadcaster(children: cs) -> cs - Ground -> [] - } -} - -fn parse_node(input: String) -> #(String, Node) { - let assert [full_name, children_str] = string.split(input, on: " -> ") - let children = string.split(children_str, on: ", ") - - case full_name { - "%" <> name -> #(name, Flipflop(children: children, state: Off)) - "&" <> name -> #(name, Conjunction(children: children, state: dict.new())) - "broadcaster" -> #("broadcaster", Broadcaster(children: children)) - name -> #(name, Ground) - } -} - -fn to_initial_state(nodes: List(#(String, Node))) -> Dict(String, Node) { - let node_dict = dict.from_list(nodes) - let node_names = dict.keys(node_dict) - - let node_dict = - node_dict - |> dict.values - |> list.map(get_children) - |> list.concat - |> set.from_list - |> set.drop(dict.keys(node_dict)) - |> set.to_list - |> list.fold(node_dict, fn(acc, n) { dict.insert(acc, n, Ground) }) - - use name, node <- dict.map_values(node_dict) - case node { - Conjunction(state: _, children: chs) -> - node_names - |> list.filter(fn(n) { - let assert Ok(node) = dict.get(node_dict, n) - list.contains(get_children(node), any: name) - }) - |> list.map(fn(n) { #(n, Low) }) - |> dict.from_list() - |> fn(dict) { Conjunction(state: dict, children: chs) } - other -> other - } -} - -fn add_to_queue(from, children, pitch, queue) { - use acc, c <- list.fold(children, queue) - queue.push_back(acc, Tone(from: from, to: c, pitch: pitch)) -} - -fn add_tones(state: State, nodes, pitch, n) { - case pitch { - Low -> - State(..state, nodes: nodes, low: state.low + n, cycle: state.cycle + 1) - High -> - State(..state, nodes: nodes, high: state.high + n, cycle: state.cycle + 1) - } -} - -fn press_button_once(initial: State, queue: Queue(Tone)) { - let State(nodes: nodes, ..) = initial - - use <- bool.guard(queue.is_empty(queue), initial) - let assert Ok(#(Tone(from_name, to_name, pitch), rest)) = - queue.pop_front(queue) - - let assert Ok(to_node) = dict.get(nodes, to_name) - case to_node { - Broadcaster(children) -> { - let new_state = - add_tones(initial, nodes, pitch, list.length(children) + 1) - - let new_queue = add_to_queue(to_name, children, pitch, rest) - press_button_once(new_state, new_queue) - } - - Conjunction(state: state, children: children) -> { - let new_state = - state - |> dict.insert(from_name, pitch) - - let updated_nodes = - Conjunction(state: new_state, children: children) - |> dict.insert(nodes, to_name, _) - - let pitch_out = combinator_pitch(new_state) - - let new_state = - add_tones(initial, updated_nodes, pitch_out, list.length(children)) - |> check_for_interesting_node(from_name, pitch_out) - - add_to_queue(to_name, children, pitch_out, rest) - |> press_button_once(new_state, _) - } - - Flipflop(..) if pitch == High -> - press_button_once(State(..initial, cycle: initial.cycle + 1), rest) - - Flipflop(state: state, children: children) -> { - let updated_nodes = - Flipflop(state: flip_power(state), children: children) - |> dict.insert(nodes, to_name, _) - - let pitch_out = flip_flop_pitch(state) - let new_state = - add_tones(initial, updated_nodes, pitch_out, list.length(children)) - - add_to_queue(to_name, children, flip_flop_pitch(state), rest) - |> press_button_once(new_state, _) - } - - Ground(..) -> - press_button_once(State(..initial, cycle: initial.cycle + 1), rest) - } -} - -pub fn part1(input: String) { - let initial_state = - input - |> string.split(on: "\n") - |> list.map(parse_node) - |> to_initial_state() - - iterator.iterate( - from: State(initial_state, 0, 0, 1, dict.new()), - with: press_button_once(_, queue.from_list([ - Tone("button", "broadcaster", Low), - ])), - ) - |> iterator.at(1000) - |> fn(s) { - let assert Ok(State(high: high, low: low, ..)) = s - high * low - } - |> string.inspect -} - -fn check_for_interesting_node(state, name, pitch_out) { - case name, pitch_out { - "rk", High | "cd", High | "zf", High | "qx", High -> - State( - ..state, - sentry_nodes: dict.insert(state.sentry_nodes, name, state.cycle), - ) - _, _ -> state - } -} - -pub fn part2(input: String) { - let initial_state = - input - |> string.split(on: "\n") - |> list.map(parse_node) - |> to_initial_state() - - iterator.iterate( - from: State(initial_state, 0, 0, 1, dict.new()), - with: press_button_once(_, queue.from_list([ - Tone("button", "broadcaster", Low), - ])), - ) - |> iterator.drop_while(fn(s) { dict.size(s.sentry_nodes) < 4 }) - |> iterator.step - |> fn(s: Step(State, Iterator(State))) { - let assert Next(goal, _rest) = s - goal.sentry_nodes - } - |> string.inspect -} - -pub fn main() { - let assert Ok(part) = adglent.get_part() - let assert Ok(input) = adglent.get_input("20") - case part { - First -> - part1(input) - |> adglent.inspect - |> io.println - Second -> - part2(input) - |> adglent.inspect - |> io.println - } -} diff --git a/aoc2023-gleam/src/day21/.gitignore b/aoc2023-gleam/src/day21/.gitignore deleted file mode 100644 index ae40cea..0000000 --- a/aoc2023-gleam/src/day21/.gitignore +++ /dev/null @@ -1 +0,0 @@ -input.txt \ No newline at end of file diff --git a/aoc2023-gleam/src/day21/solve.gleam b/aoc2023-gleam/src/day21/solve.gleam deleted file mode 100644 index 4d5c246..0000000 --- a/aoc2023-gleam/src/day21/solve.gleam +++ /dev/null @@ -1,25 +0,0 @@ -import adglent.{First, Second} -import gleam/io - -pub fn part1(input: String) { - todo as "Implement solution to part 1" -} - -pub fn part2(input: String) { - todo as "Implement solution to part 2" -} - -pub fn main() { - let assert Ok(part) = adglent.get_part() - let assert Ok(input) = adglent.get_input("21") - case part { - First -> - part1(input) - |> adglent.inspect - |> io.println - Second -> - part2(input) - |> adglent.inspect - |> io.println - } -} diff --git a/aoc2023-gleam/src/day22/.gitignore b/aoc2023-gleam/src/day22/.gitignore deleted file mode 100644 index ae40cea..0000000 --- a/aoc2023-gleam/src/day22/.gitignore +++ /dev/null @@ -1 +0,0 @@ -input.txt \ No newline at end of file diff --git a/aoc2023-gleam/src/day22/solve.gleam b/aoc2023-gleam/src/day22/solve.gleam deleted file mode 100644 index 7bf2fb4..0000000 --- a/aoc2023-gleam/src/day22/solve.gleam +++ /dev/null @@ -1,199 +0,0 @@ -import adglent.{First, Second} -import gleam/bool -import gleam/dict.{type Dict} -import gleam/int -import gleam/io -import gleam/list -import gleam/option.{None, Some} -import gleam/regex -import gleam/result -import gleam/set.{type Set} -import gleam/string - -type Point { - Point(x: Int, y: Int, z: Int) -} - -fn down_one(p: Point) -> Point { - Point(..p, z: p.z - 1) -} - -type Block { - Block(index: Int, from: Point, to: Point) -} - -fn compare_blocks(b1: Block, b2: Block) { - int.compare(b1.to.z, b2.to.z) -} - -type Space = - Dict(Point, Block) - -type AllBlocks = - Dict(Block, List(Point)) - -type BlockTree = - Dict(Int, Set(Int)) - -fn parse_block(index: Int, input: String) -> Block { - let assert Ok(re) = regex.from_string("(.*),(.*),(.*)~(.*),(.*),(.*)") - - let assert [scan] = regex.scan(with: re, content: input) - - let assert [x1, y1, z1, x2, y2, z2] = - scan.submatches - |> option.all - |> option.unwrap([]) - |> list.map(int.parse) - |> result.values - Block(index: index, from: Point(x1, y1, z1), to: Point(x2, y2, z2)) -} - -fn cross_section_at_level(b: Block, z: Int) -> List(Point) { - use x <- list.flat_map(list.range(b.from.x, b.to.x)) - use y <- list.map(list.range(b.from.y, b.to.y)) - Point(x, y, z) -} - -fn place_block(space: Space, b: Block, z: Int) -> Space { - let now_occupied = { - use x <- list.flat_map(list.range(b.from.x, b.to.x)) - use y <- list.flat_map(list.range(b.from.y, b.to.y)) - use z <- list.map(list.range(z, z + b.to.z - b.from.z)) - #(Point(x, y, z), b) - } - - dict.merge(space, dict.from_list(now_occupied)) -} - -fn find_lowest_level(space: Space, b: Block) -> Space { - do_find_lowest(space, b, b.from.z) -} - -fn do_find_lowest(space: Space, b: Block, z: Int) -> Space { - let is_intersecting = - list.any(cross_section_at_level(b, z), dict.has_key(space, _)) - - case z, is_intersecting { - 0, _ -> place_block(space, b, 1) - _, True -> place_block(space, b, z + 1) - _, False -> do_find_lowest(space, b, z - 1) - } -} - -fn to_block_positions(space: Space) -> AllBlocks { - use acc, point, index <- dict.fold(space, dict.new()) - use points <- dict.update(acc, index) - case points { - Some(ps) -> [point, ..ps] - None -> [point] - } -} - -fn above_blocks(blocks: AllBlocks) -> BlockTree { - use acc, block, points <- dict.fold(blocks, dict.new()) - use _ <- dict.update(acc, block.index) - { - use above_block, above_points <- dict.filter(blocks) - above_block.index != block.index - && list.any(above_points, fn(p) { list.contains(points, down_one(p)) }) - } - |> dict.keys - |> list.map(fn(b) { b.index }) - |> set.from_list -} - -fn below_blocks(blocktree: BlockTree) -> BlockTree { - use acc, block, _ <- dict.fold(blocktree, dict.new()) - use _ <- dict.update(acc, block) - { - use _, aboves <- dict.filter(blocktree) - set.contains(aboves, block) - } - |> dict.keys - |> set.from_list -} - -fn vulnerable_blocks(below_tree: BlockTree) -> List(Int) { - use block <- list.filter(dict.keys(below_tree)) - use bs <- list.any(dict.values(below_tree)) - !{ set.size(bs) == 0 } && { set.size(set.delete(bs, block)) == 0 } -} - -pub fn part1(input: String) { - let settled_blocks = - input - |> string.split("\n") - |> list.index_map(parse_block) - |> list.sort(compare_blocks) - |> list.fold(dict.new(), find_lowest_level) - - let block_positions = to_block_positions(settled_blocks) - let above_blocks = above_blocks(block_positions) - let below_blocks = below_blocks(above_blocks) - - let vulnerable_blocks = vulnerable_blocks(below_blocks) - - list.length(dict.keys(block_positions)) - list.length(vulnerable_blocks) -} - -fn all_falling_blocks(n: Int, above: BlockTree, below: BlockTree) { - let starting_set = set.insert(set.new(), n) - do_falling_blocks(starting_set, starting_set, above, below) -} - -fn do_falling_blocks( - fallen: Set(Int), - blocks: Set(Int), - above: BlockTree, - below: BlockTree, -) -> Int { - use <- bool.guard(set.size(blocks) == 0, set.size(fallen) - 1) - - let blocks_above = - { - use block <- list.flat_map(set.to_list(blocks)) - let assert Ok(supports) = dict.get(above, block) - use support <- list.filter(set.to_list(supports)) - let assert Ok(supportings) = dict.get(below, support) - use supporting <- list.all(set.to_list(supportings)) - set.contains(fallen, supporting) - } - |> set.from_list() - - set.union(fallen, blocks_above) - |> do_falling_blocks(blocks_above, above, below) -} - -pub fn part2(input: String) { - let settled_blocks = - input - |> string.split("\n") - |> list.index_map(parse_block) - |> list.sort(compare_blocks) - |> list.fold(dict.new(), find_lowest_level) - - let block_positions = to_block_positions(settled_blocks) - let above_blocks = above_blocks(block_positions) - let below_blocks = below_blocks(above_blocks) - - let vulnerable_blocks = vulnerable_blocks(below_blocks) - - use acc, b <- list.fold(vulnerable_blocks, 0) - acc + all_falling_blocks(b, above_blocks, below_blocks) -} - -pub fn main() { - let assert Ok(part) = adglent.get_part() - let assert Ok(input) = adglent.get_input("22") - case part { - First -> - part1(input) - |> adglent.inspect - |> io.println - Second -> - part2(input) - |> adglent.inspect - |> io.println - } -} diff --git a/aoc2023-gleam/src/day23/.gitignore b/aoc2023-gleam/src/day23/.gitignore deleted file mode 100644 index ae40cea..0000000 --- a/aoc2023-gleam/src/day23/.gitignore +++ /dev/null @@ -1 +0,0 @@ -input.txt \ No newline at end of file diff --git a/aoc2023-gleam/src/day23/solve.gleam b/aoc2023-gleam/src/day23/solve.gleam deleted file mode 100644 index e1fe638..0000000 --- a/aoc2023-gleam/src/day23/solve.gleam +++ /dev/null @@ -1,194 +0,0 @@ -import adglent.{First, Second} -import gleam/int -import gleam/io -import gleam/dict.{type Dict} -import gleam/list -import gleam/option.{type Option, None, Some} -import gleam/string -import gleam/set.{type Set} -import gleam/bool -import utilities/array2d.{type Array2D, type Posn, Posn} - -type Path { - Unknown - Straight - Junction -} - -type Route { - Route(to: Posn, distance: Int) -} - -fn append_to_key(v: Option(List(a)), new: a) -> List(a) { - case v { - None -> [new] - Some(xs) -> [new, ..xs] - } -} - -fn first_parse_path(c: String) -> Result(Path, Nil) { - case c { - "#" -> Error(Nil) - _ -> Ok(Unknown) - } -} - -fn junction_neighbors(p: Posn) -> List(Posn) { - [Posn(..p, r: p.r + 1), Posn(..p, c: p.c + 1)] -} - -fn mark_junctions(trails: Array2D(Path)) -> Array2D(Path) { - use trail, _ <- dict.map_values(trails) - - let valid_neighbors = - trail - |> array2d.ortho_neighbors - |> list.filter(dict.has_key(trails, _)) - - case list.length(valid_neighbors) { - 2 -> Straight - _ -> Junction - } -} - -fn start_walking_to_next_junction( - start: Posn, - next: Posn, - trails: Array2D(Path), -) { - let seen = - set.new() - |> set.insert(start) - |> set.insert(next) - walk_to_next_junction(start, next, 1, seen, trails) -} - -fn walk_to_next_junction( - start: Posn, - current: Posn, - length: Int, - seen: Set(Posn), - trails: Array2D(Path), -) -> #(Posn, Route) { - let assert [next] = - current - |> array2d.ortho_neighbors - |> list.filter(fn(n) { dict.has_key(trails, n) && !set.contains(seen, n) }) - - case dict.get(trails, next) { - Ok(Junction) -> #(start, Route(to: next, distance: length + 1)) - _ -> { - let seen = set.insert(seen, current) - walk_to_next_junction(start, next, { length + 1 }, seen, trails) - } - } -} - -fn find_routes(junctions, trails) { - use junction <- list.flat_map(junctions) - use neighbor <- list.filter_map(junction_neighbors(junction)) - case dict.has_key(trails, neighbor) { - True -> Ok(start_walking_to_next_junction(junction, neighbor, trails)) - False -> Error(Nil) - } -} - -fn generate_routes( - junctions: List(Posn), - trails: Array2D(Path), -) -> Dict(Posn, List(Route)) { - use acc, #(from, route) <- list.fold( - find_routes(junctions, trails), - dict.new(), - ) - dict.update(acc, from, append_to_key(_, route)) -} - -fn generate_2way_routes( - junctions: List(Posn), - trails: Array2D(Path), -) -> Dict(Posn, List(Route)) { - use acc, #(from, route) <- list.fold( - find_routes(junctions, trails), - dict.new(), - ) - acc - |> dict.update(from, append_to_key(_, route)) - |> dict.update(route.to, append_to_key(_, Route(from, route.distance))) -} - -fn dfs(routes, from, to) { - let seen = set.insert(set.new(), from) - do_dfs(routes, from, to, 0, seen) -} - -fn do_dfs( - routes: Dict(Posn, List(Route)), - from: Posn, - to: Posn, - acc: Int, - seen: Set(Posn), -) -> Int { - use <- bool.guard(to == from, acc) - - let assert Ok(all_routes) = dict.get(routes, from) - let neighbors = list.filter(all_routes, fn(r) { !set.contains(seen, r.to) }) - - case neighbors { - [] -> 0 - neighbors -> - list.fold(neighbors, acc, fn(inner_acc, n) { - let score = - do_dfs(routes, n.to, to, acc + n.distance, set.insert(seen, n.to)) - int.max(score, inner_acc) - }) - } -} - -fn solve_using( - input: String, - using: fn(List(Posn), Dict(Posn, Path)) -> Dict(Posn, List(Route)), -) -> Int { - let min_row = 0 - let max_row = list.length(string.split(input, "\n")) - 1 - - let trails = - input - |> array2d.parse_grid_using(first_parse_path) - |> mark_junctions - - let junctions = - trails - |> dict.filter(fn(_, v) { v == Junction }) - |> dict.keys - - let assert Ok(start) = list.find(junctions, fn(j) { j.r == min_row }) - let assert Ok(end) = list.find(junctions, fn(j) { j.r == max_row }) - - let routes = using(junctions, trails) - - dfs(routes, start, end) -} - -pub fn part1(input: String) { - solve_using(input, generate_routes) -} - -pub fn part2(input: String) { - solve_using(input, generate_2way_routes) -} - -pub fn main() { - let assert Ok(part) = adglent.get_part() - let assert Ok(input) = adglent.get_input("23") - case part { - First -> - part1(input) - |> adglent.inspect - |> io.println - Second -> - part2(input) - |> adglent.inspect - |> io.println - } -} diff --git a/aoc2023-gleam/src/day3/.gitignore b/aoc2023-gleam/src/day3/.gitignore deleted file mode 100644 index ae40cea..0000000 --- a/aoc2023-gleam/src/day3/.gitignore +++ /dev/null @@ -1 +0,0 @@ -input.txt \ No newline at end of file diff --git a/aoc2023-gleam/src/day3/solve.gleam b/aoc2023-gleam/src/day3/solve.gleam deleted file mode 100644 index ad975aa..0000000 --- a/aoc2023-gleam/src/day3/solve.gleam +++ /dev/null @@ -1,180 +0,0 @@ -import adglent.{First, Second} -import gleam/io -import gleam/dict.{type Dict} -import gleam/string -import gleam/list -import gleam/int -import gleam/order.{type Order, Eq} - -type Coord { - Coord(x: Int, y: Int) -} - -type SymbolKind { - Gear - SomethingElse -} - -type Symbol { - Number(Int) - Symbol(SymbolKind) - Empty -} - -type Board = - Dict(Coord, Symbol) - -type Cell { - Cell(coord: Coord, symbol: Symbol) -} - -type Part { - Part(coords: List(Coord), part_number: Int) -} - -fn to_symbol(c: String) -> Symbol { - case int.parse(c), c { - Ok(n), _ -> Number(n) - _, "." -> Empty - _, "*" -> Symbol(Gear) - _, _ -> Symbol(SomethingElse) - } -} - -fn to_board(input: String) -> Board { - { - use y, r <- list.index_map(string.split(input, "\n")) - use x, c <- list.index_map(string.to_graphemes(r)) - #(Coord(x, y), to_symbol(c)) - } - |> list.flatten() - |> dict.from_list() -} - -fn cell_compare(a: Cell, b: Cell) -> Order { - case int.compare(a.coord.y, b.coord.y) { - Eq -> int.compare(a.coord.x, b.coord.x) - other -> other - } -} - -fn find_all_part_digits(b: Board) -> List(Cell) { - b - |> dict.filter(fn(_, v) { - case v { - Number(_) -> True - _ -> False - } - }) - |> dict.to_list() - |> list.map(fn(tup) { Cell(tup.0, tup.1) }) - |> list.sort(cell_compare) -} - -fn to_parts(cells: List(Cell)) -> List(Part) { - do_parts(cells, []) -} - -fn do_parts(cells: List(Cell), parts: List(Part)) -> List(Part) { - case cells { - [] -> parts - [Cell(next, Number(n)), ..t] -> { - case parts { - [] -> do_parts(t, [Part([next], n), ..parts]) - [Part([prev, ..] as coords, n0), ..rest_parts] -> - case { next.x - prev.x }, { next.y - prev.y } { - 1, 0 -> - do_parts(t, [Part([next, ..coords], n0 * 10 + n), ..rest_parts]) - _, _ -> do_parts(t, [Part([next], n), ..parts]) - } - _ -> panic - } - } - _ -> panic - } -} - -fn all_neighbors(c: Coord) -> List(Coord) { - use dx <- list.flat_map([-1, 0, 1]) - use dy <- list.filter_map([-1, 0, 1]) - case dx, dy { - 0, 0 -> Error(Nil) - _, _ -> Ok(Coord(c.x + dx, c.y + dy)) - } -} - -fn sum_valid_parts(acc: Int, part: Part, board: Board) -> Int { - let neighbors = - part.coords - |> list.flat_map(all_neighbors) - |> list.unique() - - let sym = [Ok(Symbol(Gear)), Ok(Symbol(SomethingElse))] - case list.any(neighbors, fn(c) { list.contains(sym, dict.get(board, c)) }) { - True -> acc + part.part_number - False -> acc - } -} - -pub fn part1(input: String) -> Int { - let board = to_board(input) - - board - |> find_all_part_digits - |> to_parts - |> list.fold(0, fn(acc, p) { sum_valid_parts(acc, p, board) }) -} - -fn to_part_with_neighbors(part: Part) -> Part { - part.coords - |> list.flat_map(all_neighbors) - |> list.unique - |> Part(part.part_number) -} - -fn find_part_numbers_near_gear(gear: Coord, parts: List(Part)) -> List(Int) { - use part <- list.filter_map(parts) - case list.contains(part.coords, gear) { - True -> Ok(part.part_number) - False -> Error(Nil) - } -} - -fn to_sum_of_gear_ratios(adjacent_parts: List(List(Int))) -> Int { - use acc, ps <- list.fold(adjacent_parts, 0) - case ps { - [p1, p2] -> acc + p1 * p2 - _ -> acc - } -} - -pub fn part2(input: String) -> Int { - let board = to_board(input) - - let parts = - board - |> find_all_part_digits - |> to_parts - |> list.map(to_part_with_neighbors) - - board - |> dict.filter(fn(_, v) { v == Symbol(Gear) }) - |> dict.keys - |> list.map(find_part_numbers_near_gear(_, parts)) - |> to_sum_of_gear_ratios -} - -pub fn main() { - let assert Ok(part) = adglent.get_part() - let assert Ok(input) = adglent.get_input("3") - case part { - First -> - part1(input) - |> adglent.inspect - |> io.println - Second -> - part2(input) - |> adglent.inspect - |> io.println - } -} diff --git a/aoc2023-gleam/src/day4/.gitignore b/aoc2023-gleam/src/day4/.gitignore deleted file mode 100644 index ae40cea..0000000 --- a/aoc2023-gleam/src/day4/.gitignore +++ /dev/null @@ -1 +0,0 @@ -input.txt \ No newline at end of file diff --git a/aoc2023-gleam/src/day4/solve.gleam b/aoc2023-gleam/src/day4/solve.gleam deleted file mode 100644 index 34d6098..0000000 --- a/aoc2023-gleam/src/day4/solve.gleam +++ /dev/null @@ -1,98 +0,0 @@ -import adglent.{First, Second} -import gleam/bool -import gleam/dict.{type Dict} -import gleam/int -import gleam/io -import gleam/list -import gleam/option.{None, Some} -import gleam/result -import gleam/set.{type Set} -import gleam/string - -type Card { - Card(number: Int, winners: Int) -} - -fn numbers_to_set(str: String) -> Set(Int) { - str - |> string.split(" ") - |> list.map(int.parse) - |> result.values() - |> set.from_list() -} - -fn parse_card(card: String) -> Card { - let assert Ok(#("Card" <> n_str, rest)) = string.split_once(card, ": ") - let assert Ok(#(winning_str, has_str)) = string.split_once(rest, " | ") - let assert Ok(n) = int.parse(string.trim(n_str)) - - let winning = numbers_to_set(winning_str) - let has = numbers_to_set(has_str) - let winners = set.size(set.intersection(winning, has)) - - Card(number: n, winners: winners) -} - -fn win_points(n: Int) { - bool.guard(n < 2, n, fn() { 2 * win_points(n - 1) }) -} - -pub fn part1(input: String) { - use acc, c <- list.fold(string.split(input, "\n"), 0) - c - |> parse_card - |> fn(c: Card) { win_points(c.winners) } - |> int.add(acc) -} - -fn win_more_cards(cards: List(String), count: Dict(Int, Int)) { - case cards { - [] -> - count - |> dict.values - |> int.sum - [raw_card, ..rest] -> { - let card = parse_card(raw_card) - case card.winners { - 0 -> win_more_cards(rest, count) - n -> win_more_cards(rest, update_counts(n, card, count)) - } - } - } -} - -fn update_counts(n: Int, card: Card, count: Dict(Int, Int)) -> Dict(Int, Int) { - let assert Ok(bonus) = dict.get(count, card.number) - use acc, n <- list.fold(list.range(card.number + 1, card.number + n), count) - use c <- dict.update(acc, n) - case c { - Some(i) -> i + bonus - None -> panic as "won a card that doesn't exist in the card pile" - } -} - -pub fn part2(input: String) { - let cards = string.split(input, "\n") - - let count = - list.range(1, list.length(cards)) - |> list.map(fn(n) { #(n, 1) }) - |> dict.from_list() - - win_more_cards(cards, count) -} - -pub fn main() { - let assert Ok(part) = adglent.get_part() - let assert Ok(input) = adglent.get_input("4") - case part { - First -> - part1(input) - |> adglent.inspect - |> io.println - Second -> - part2(input) - |> adglent.inspect - |> io.println - } -} diff --git a/aoc2023-gleam/src/day5/.gitignore b/aoc2023-gleam/src/day5/.gitignore deleted file mode 100644 index ae40cea..0000000 --- a/aoc2023-gleam/src/day5/.gitignore +++ /dev/null @@ -1 +0,0 @@ -input.txt \ No newline at end of file diff --git a/aoc2023-gleam/src/day5/solve.gleam b/aoc2023-gleam/src/day5/solve.gleam deleted file mode 100644 index 7c05310..0000000 --- a/aoc2023-gleam/src/day5/solve.gleam +++ /dev/null @@ -1,162 +0,0 @@ -import adglent.{First, Second} -import gleam/io -import gleam/string -import gleam/result -import gleam/list.{Continue, Stop} -import gleam/int -import gleam/function - -// Types ------------------------------------------------------------------------------------------- - -pub type Almanac { - Almanac(seeds: List(Int), mappers: List(Mapper)) -} - -pub type MappingRange { - MRange(start: Int, end: Int, offset: Int) -} - -pub type SeedRange { - SRange(start: Int, end: Int) -} - -type Mapper = - List(MappingRange) - -// Parsing ----------------------------------------------------------------------------------------- - -fn parse_input(input: String) { - let assert ["seeds: " <> raw_seeds, ..raw_mappers] = - string.split(input, on: "\n\n") - - let seeds = string_to_int_list(raw_seeds) - let mappers = - list.map( - raw_mappers, - function.compose(string.split(_, on: "\n"), parse_mapper), - ) - Almanac(seeds, mappers) -} - -fn string_to_int_list(str: String) { - str - |> string.split(on: " ") - |> list.map(int.parse) - |> result.values -} - -fn parse_mapper(strs: List(String)) -> Mapper { - let assert [_, ..raw_ranges] = strs - list.map(raw_ranges, parse_mrange) - |> list.sort(fn(a, b) { int.compare(a.start, b.start) }) -} - -fn parse_mrange(str: String) -> MappingRange { - let assert [destination, source, range_width] = string_to_int_list(str) - MRange(source, source + range_width - 1, destination - source) -} - -// Part 1 ------------------------------------------------------------------------------------------ - -pub fn part1(input: String) { - let Almanac(seeds, mappers) = parse_input(input) - - list.map(seeds, list.fold(over: mappers, from: _, with: correspond)) - |> list.reduce(int.min) - |> result.unwrap(0) - |> string.inspect -} - -fn correspond(n: Int, mapper: Mapper) { - use acc, mrange <- list.fold_until(over: mapper, from: n) - case mrange.start <= acc && acc <= mrange.end { - True -> Stop(acc + mrange.offset) - False -> Continue(acc) - } -} - -// Part 2 ------------------------------------------------------------------------------------------ - -pub fn part2(input: String) { - let Almanac(seeds, mappers) = parse_input(input) - - let assert [SRange(answer, _), ..] = - seeds - |> list.sized_chunk(into: 2) - |> list.map(fn(chunk) { - let assert [start, length] = chunk - [SRange(start, start + length - 1)] - |> remap_all_seed_ranges(mappers) - }) - |> list.flatten() - |> list.sort(fn(a, b) { int.compare(a.start, b.start) }) - - string.inspect(answer) -} - -fn remap_all_seed_ranges(srs: List(SeedRange), mappers: List(Mapper)) { - case mappers { - [] -> srs - [mapper, ..rest] -> - list.flat_map(srs, remap_range(_, mapper)) - |> remap_all_seed_ranges(rest) - } -} - -fn remap_range(r: SeedRange, mapper: Mapper) -> List(SeedRange) { - do_remap_range(r, mapper, []) -} - -fn transform_range(r: SeedRange, mapper: MappingRange) -> SeedRange { - SRange(r.start + mapper.offset, r.end + mapper.offset) -} - -fn do_remap_range(r: SeedRange, mapper: Mapper, acc: List(SeedRange)) { - case mapper { - // no more mappings -> no mapping covers this range - [] -> [r, ..acc] - // range is to the left of current mapping -> no mapping covers this range - [m, ..] if r.end < m.start -> [r, ..acc] - // range is to the right of current mapping -> move to next mapping - [m, ..ms] if r.start > m.end -> do_remap_range(r, ms, acc) - // range is fully inside mapping -> range is transformed - [m, ..] if r.start >= m.start && r.end <= m.end -> [ - transform_range(r, m), - ..acc - ] - // range overlaps start but not end -> left side not transformed, right side transformed - [m, ..] if r.start < m.start && r.end <= m.end -> [ - SRange(r.start, m.start - 1), - transform_range(SRange(m.start, r.end), m), - ..acc - ] - // range overlaps end but not start -> left side transformed, right side moves to next mapping - [m, ..ms] if r.start >= m.start && r.end > m.end -> - do_remap_range(SRange(m.end + 1, r.end), ms, [ - transform_range(SRange(r.start, m.end), m), - ..acc - ]) - // mapping is fully inside range -> left not transformed, middle transformed, right to next - [m, ..ms] -> - do_remap_range(SRange(m.end + 1, r.end), ms, [ - SRange(r.start, m.start - 1), - transform_range(SRange(m.start, m.end), m), - ..acc - ]) - } -} - -pub fn main() { - let assert Ok(part) = adglent.get_part() - let assert Ok(input) = adglent.get_input("5") - case part { - First -> - part1(input) - |> adglent.inspect - |> io.println - Second -> - part2(input) - |> adglent.inspect - |> io.println - } -} diff --git a/aoc2023-gleam/src/day6/.gitignore b/aoc2023-gleam/src/day6/.gitignore deleted file mode 100644 index ae40cea..0000000 --- a/aoc2023-gleam/src/day6/.gitignore +++ /dev/null @@ -1 +0,0 @@ -input.txt \ No newline at end of file diff --git a/aoc2023-gleam/src/day6/solve.gleam b/aoc2023-gleam/src/day6/solve.gleam deleted file mode 100644 index 88044c4..0000000 --- a/aoc2023-gleam/src/day6/solve.gleam +++ /dev/null @@ -1,85 +0,0 @@ -import adglent.{First, Second} -import gleam/io -import gleam/string -import gleam/int -import gleam/list -import gleam/result - -type Race { - Race(time: Int, distance: Int) -} - -fn parse_with_bad_kerning(input: String) { - input - |> string.split("\n") - |> list.map(fn(str) { - str - |> string.split(" ") - |> list.map(int.parse) - |> result.values - }) - |> list.transpose - |> list.map(fn(ns) { - let assert [t, d] = ns - Race(t, d) - }) -} - -fn find_bound(race: Race, button_time: Int, step: Int) { - let travel_time = race.time - button_time - case button_time * travel_time > race.distance { - True -> button_time - False -> find_bound(race, button_time + step, step) - } -} - -fn lower_bound(race: Race) { - find_bound(race, 1, 1) -} - -fn upper_bound(race: Race) { - find_bound(race, race.time, -1) -} - -pub fn part1(input: String) { - { - use acc, race <- list.fold(parse_with_bad_kerning(input), 1) - acc * { upper_bound(race) - lower_bound(race) + 1 } - } - |> string.inspect -} - -fn parse_properly(input: String) { - input - |> string.replace(" ", "") - |> string.split("\n") - |> list.flat_map(string.split(_, ":")) - |> list.map(int.parse) - |> result.values -} - -pub fn part2(input: String) { - let assert [time, distance] = - input - |> parse_properly - - let race = Race(time, distance) - - upper_bound(race) - lower_bound(race) + 1 - |> string.inspect -} - -pub fn main() { - let assert Ok(part) = adglent.get_part() - let assert Ok(input) = adglent.get_input("6") - case part { - First -> - part1(input) - |> adglent.inspect - |> io.println - Second -> - part2(input) - |> adglent.inspect - |> io.println - } -} diff --git a/aoc2023-gleam/src/day7/.gitignore b/aoc2023-gleam/src/day7/.gitignore deleted file mode 100644 index ae40cea..0000000 --- a/aoc2023-gleam/src/day7/.gitignore +++ /dev/null @@ -1 +0,0 @@ -input.txt \ No newline at end of file diff --git a/aoc2023-gleam/src/day7/solve.gleam b/aoc2023-gleam/src/day7/solve.gleam deleted file mode 100644 index 4454883..0000000 --- a/aoc2023-gleam/src/day7/solve.gleam +++ /dev/null @@ -1,140 +0,0 @@ -import adglent.{First, Second} -import gleam/bool -import gleam/function -import gleam/int -import gleam/io -import gleam/list -import gleam/order.{type Order, Eq, Lt} -import gleam/string - -// Types ------------------------------------------------------------------------------------------- - -type Hand { - Hand(cards: List(Int), wager: Int) -} - -// Common functions -------------------------------------------------------------------------------- - -fn parse_hand(str: String) -> Hand { - let assert [cards, wager] = string.split(str, " ") - let cards = - string.to_graphemes(cards) - |> list.map(card_rank) - let assert Ok(wager) = int.parse(wager) - - Hand(cards, wager) -} - -fn classify_hand(hand: Hand) -> Int { - case list.length(list.unique(hand.cards)), card_counts(hand) { - 1, _ -> 8 - 2, [1, 4] -> 7 - 2, [2, 3] -> 6 - 3, [1, 1, 3] -> 5 - 3, [1, 2, 2] -> 4 - 4, _ -> 3 - 5, _ -> 2 - _, _ -> 1 - } -} - -fn card_counts(hand: Hand) { - hand.cards - |> list.sort(int.compare) - |> list.chunk(function.identity) - |> list.map(list.length) - |> list.sort(int.compare) -} - -fn card_rank(card: String) -> Int { - case int.parse(card), card { - Ok(n), _ -> n - _, "A" -> 14 - _, "K" -> 13 - _, "Q" -> 12 - _, "J" -> 11 - _, "T" -> 10 - _, _ -> 1 - } -} - -fn compare_hands(hand1: Hand, hand2: Hand, using: fn(Hand) -> Int) -> Order { - case int.compare(using(hand1), using(hand2)) { - Eq -> compare_top_card(hand1.cards, hand2.cards) - other -> other - } -} - -fn compare_top_card(cards1: List(Int), cards2: List(Int)) -> Order { - use <- bool.guard(cards1 == [] || cards2 == [], Eq) - let assert [c1, ..rest1] = cards1 - let assert [c2, ..rest2] = cards2 - case int.compare(c1, c2) { - Eq -> compare_top_card(rest1, rest2) - other -> other - } -} - -fn part(input: String, comparator: fn(Hand, Hand) -> Order) { - input - |> string.split("\n") - |> list.map(parse_hand) - |> list.sort(comparator) - |> list.index_map(fn(i, h) { { i + 1 } * h.wager }) - |> int.sum - |> string.inspect -} - -// Part 1 ------------------------------------------------------------------------------------------ - -pub fn part1(input: String) { - part(input, compare_without_wilds) -} - -fn compare_without_wilds(hand1: Hand, hand2: Hand) { - compare_hands(hand1, hand2, classify_hand) -} - -// Part 2 ------------------------------------------------------------------------------------------ - -pub fn part2(input: String) { - part(string.replace(input, "J", "*"), compare_hands_considering_jokers) -} - -fn find_best_joker_substitution(hand: Hand) { - use acc, card <- list.fold(list.range(2, 14), Hand([], 0)) - let subbed_cards = { - use c <- list.map(hand.cards) - case c { - 1 -> card - other -> other - } - } - let subbed_hand = Hand(..hand, cards: subbed_cards) - case compare_hands(acc, subbed_hand, classify_hand) { - Lt -> subbed_hand - _ -> acc - } -} - -fn compare_hands_considering_jokers(hand1: Hand, hand2: Hand) -> Order { - use hand <- compare_hands(hand1, hand2) - hand - |> find_best_joker_substitution - |> classify_hand -} - -pub fn main() { - let assert Ok(part) = adglent.get_part() - let assert Ok(input) = adglent.get_input("7") - case part { - First -> - part1(input) - |> adglent.inspect - |> io.println - Second -> - part2(input) - |> adglent.inspect - |> io.println - } -} diff --git a/aoc2023-gleam/src/day8/.gitignore b/aoc2023-gleam/src/day8/.gitignore deleted file mode 100644 index ae40cea..0000000 --- a/aoc2023-gleam/src/day8/.gitignore +++ /dev/null @@ -1 +0,0 @@ -input.txt \ No newline at end of file diff --git a/aoc2023-gleam/src/day8/solve.gleam b/aoc2023-gleam/src/day8/solve.gleam deleted file mode 100644 index 6b36e2d..0000000 --- a/aoc2023-gleam/src/day8/solve.gleam +++ /dev/null @@ -1,91 +0,0 @@ -import adglent.{First, Second} -import gleam/bool -import gleam/dict.{type Dict} -import gleam/io -import gleam/iterator.{type Iterator, Next} -import gleam/list -import gleam/option.{Some} -import gleam/string -import gleam/regex.{type Match, Match} -import gleam_community/maths/arithmetics - -type Paths { - Paths(to_left: String, to_right: String) -} - -type Maze = - Dict(String, Paths) - -fn parse(input: String) -> #(Iterator(String), Dict(String, Paths)) { - let assert [directions_str, maze_str] = string.split(input, "\n\n") - - let directions = - directions_str - |> string.to_graphemes() - |> iterator.from_list - |> iterator.cycle - - let assert Ok(re) = regex.from_string("(...) = \\((...), (...)\\)") - let maze = - maze_str - |> string.split("\n") - |> list.map(fn(str) { - let assert [Match(submatches: [Some(name), Some(left), Some(right)], ..)] = - regex.scan(re, str) - #(name, Paths(left, right)) - }) - |> dict.from_list - - #(directions, maze) -} - -fn to_next_step( - current: String, - stop_at: String, - count: Int, - directions: Iterator(String), - maze: Maze, -) -> Int { - use <- bool.guard(string.ends_with(current, stop_at), count) - let assert Next(next_direction, rest_directions) = iterator.step(directions) - let assert Ok(paths) = dict.get(maze, current) - case next_direction { - "L" -> paths.to_left - "R" -> paths.to_right - _ -> panic as "bad direction" - } - |> to_next_step(stop_at, count + 1, rest_directions, maze) -} - -pub fn part1(input: String) -> Int { - let #(directions, maze) = parse(input) - - to_next_step("AAA", "ZZZ", 0, directions, maze) -} - -pub fn part2(input: String) -> Int { - let #(directions, maze) = parse(input) - - use acc, name <- list.fold(dict.keys(maze), 1) - case string.ends_with(name, "A") { - False -> acc - True -> - to_next_step(name, "Z", 0, directions, maze) - |> arithmetics.lcm(acc) - } -} - -pub fn main() { - let assert Ok(part) = adglent.get_part() - let assert Ok(input) = adglent.get_input("8") - case part { - First -> - part1(input) - |> adglent.inspect - |> io.println - Second -> - part2(input) - |> adglent.inspect - |> io.println - } -} diff --git a/aoc2023-gleam/src/day9/.gitignore b/aoc2023-gleam/src/day9/.gitignore deleted file mode 100644 index ae40cea..0000000 --- a/aoc2023-gleam/src/day9/.gitignore +++ /dev/null @@ -1 +0,0 @@ -input.txt \ No newline at end of file diff --git a/aoc2023-gleam/src/day9/solve.gleam b/aoc2023-gleam/src/day9/solve.gleam deleted file mode 100644 index a2cc7ae..0000000 --- a/aoc2023-gleam/src/day9/solve.gleam +++ /dev/null @@ -1,70 +0,0 @@ -import adglent.{First, Second} -import gleam/io -import gleam/list -import gleam/string -import gleam/int - -fn parse(input: String, backwards backwards: Bool) -> List(List(Int)) { - use line <- list.map(string.split(input, "\n")) - use n_str <- list.map(maybe_backwards(string.split(line, " "), backwards)) - let assert Ok(n) = int.parse(n_str) - n -} - -fn maybe_backwards(xs: List(a), backwards: Bool) -> List(a) { - case backwards { - False -> list.reverse(xs) - True -> xs - } -} - -fn is_constant(ns: List(Int)) -> Bool { - case list.unique(ns) { - [_] -> True - _ -> False - } -} - -fn take_derivative(ns: List(Int)) -> List(Int) { - ns - |> list.window_by_2 - |> list.map(fn(tup) { tup.0 - tup.1 }) -} - -fn extrapolate(ns: List(Int)) { - case is_constant(ns), ns { - True, [n, ..] -> n - False, [n, ..] -> n + extrapolate(take_derivative(ns)) - _, _ -> panic as "list empty when it shouldn't be" - } -} - -fn part(input: String, backwards backwards: Bool) { - input - |> parse(backwards: backwards) - |> list.fold(0, fn(acc, ns) { extrapolate(ns) + acc }) - |> string.inspect -} - -pub fn part1(input: String) { - part(input, backwards: False) -} - -pub fn part2(input: String) { - part(input, backwards: True) -} - -pub fn main() { - let assert Ok(part) = adglent.get_part() - let assert Ok(input) = adglent.get_input("9") - case part { - First -> - part1(input) - |> adglent.inspect - |> io.println - Second -> - part2(input) - |> adglent.inspect - |> io.println - } -} diff --git a/aoc2023-gleam/src/utilities/array2d.gleam b/aoc2023-gleam/src/utilities/array2d.gleam deleted file mode 100644 index 8538129..0000000 --- a/aoc2023-gleam/src/utilities/array2d.gleam +++ /dev/null @@ -1,74 +0,0 @@ -import gleam/list -import gleam/dict.{type Dict} -import gleam/string -import gleam/int -import gleam/result - -pub type Posn { - Posn(r: Int, c: Int) -} - -pub type Array2D(a) = - Dict(Posn, a) - -pub fn add_posns(p1: Posn, p2: Posn) -> Posn { - case p1, p2 { - Posn(r1, c1), Posn(r2, c2) -> Posn(r1 + r2, c1 + c2) - } -} - -pub fn ortho_neighbors(p: Posn) -> List(Posn) { - let Posn(r, c) = p - [Posn(r + 1, c), Posn(r - 1, c), Posn(r, c + 1), Posn(r, c - 1)] -} - -pub fn to_2d_array(xss: List(List(a))) -> Array2D(a) { - to_2d_array_using(xss, fn(x) { Ok(x) }) -} - -pub fn to_2d_array_using( - xss: List(List(a)), - f: fn(a) -> Result(b, Nil), -) -> Array2D(b) { - { - use r, row <- list.index_map(xss) - use c, cell <- list.index_map(row) - case f(cell) { - Ok(contents) -> Ok(#(Posn(r, c), contents)) - Error(Nil) -> Error(Nil) - } - } - |> list.flatten - |> result.values - |> dict.from_list -} - -pub fn to_2d_intarray(xss: List(List(String))) -> Array2D(Int) { - { - use r, row <- list.index_map(xss) - use c, cell <- list.index_map(row) - let assert Ok(n) = int.parse(cell) - #(Posn(r, c), n) - } - |> list.flatten - |> dict.from_list -} - -pub fn to_list_of_lists(str: String) -> List(List(String)) { - str - |> string.split("\n") - |> list.map(string.to_graphemes) -} - -pub fn parse_grid(str: String) -> Array2D(String) { - parse_grid_using(str, fn(x) { Ok(x) }) -} - -pub fn parse_grid_using( - str: String, - f: fn(String) -> Result(a, Nil), -) -> Array2D(a) { - str - |> to_list_of_lists - |> to_2d_array_using(f) -} diff --git a/aoc2023-gleam/src/utilities/memo.gleam b/aoc2023-gleam/src/utilities/memo.gleam deleted file mode 100644 index b06d8fd..0000000 --- a/aoc2023-gleam/src/utilities/memo.gleam +++ /dev/null @@ -1,57 +0,0 @@ -import gleam/dict.{type Dict} -import gleam/otp/actor.{type Next, Continue, Stop} -import gleam/erlang/process.{type Subject, Normal} -import gleam/option.{None} - -const timeout = 1000 - -type Message(k, v) { - Shutdown - Get(key: k, client: Subject(Result(v, Nil))) - Set(key: k, value: v) -} - -type Server(k, v) = - Subject(Message(k, v)) - -pub opaque type Cache(k, v) { - Cache(server: Server(k, v)) -} - -fn handle_message( - message: Message(k, v), - dict: Dict(k, v), -) -> Next(Message(k, v), Dict(k, v)) { - case message { - Shutdown -> Stop(Normal) - Get(key, client) -> { - process.send(client, dict.get(dict, key)) - Continue(dict, None) - } - Set(key, value) -> Continue(dict.insert(dict, key, value), None) - } -} - -pub fn create(apply fun: fn(Cache(k, v)) -> t) -> t { - let assert Ok(server) = actor.start(dict.new(), handle_message) - let result = fun(Cache(server)) - process.send(server, Shutdown) - result -} - -pub fn set(in cache: Cache(k, v), for key: k, insert value: v) -> Nil { - process.send(cache.server, Set(key, value)) -} - -pub fn get(from cache: Cache(k, v), fetch key: k) -> Result(v, Nil) { - process.call(cache.server, fn(c) { Get(key, c) }, timeout) -} - -pub fn memoize(with cache: Cache(k, v), this key: k, apply fun: fn() -> v) -> v { - let result = case get(from: cache, fetch: key) { - Ok(value) -> value - Error(Nil) -> fun() - } - set(in: cache, for: key, insert: result) - result -} diff --git a/aoc2023-gleam/src/utilities/prioqueue.gleam b/aoc2023-gleam/src/utilities/prioqueue.gleam deleted file mode 100644 index abf21b9..0000000 --- a/aoc2023-gleam/src/utilities/prioqueue.gleam +++ /dev/null @@ -1,64 +0,0 @@ -//adapted from https://github.com/byronanderson/adventofcode2021/blob/main/gleam_advent/src/priority_queue.gleam - -import gleam/dict.{type Dict} - -type Ref - -@external(erlang, "erlang", "make_ref") -fn make_ref() -> Ref - -type PQueue(a) - -pub opaque type PriorityQueue(a) { - PriorityQueue(queue: PQueue(#(a, Ref)), refs: Dict(a, Ref)) -} - -type OutResult(a) { - Empty - Value(a, Int) -} - -@external(erlang, "pqueue2", "new") -fn new_() -> PQueue(a) - -@external(erlang, "pqueue2", "in") -fn insert_(item: a, prio: Int, queue: PQueue(a)) -> PQueue(a) - -@external(erlang, "pqueue2", "pout") -fn pop_(queue: PQueue(a)) -> #(OutResult(a), PQueue(a)) - -pub fn new() -> PriorityQueue(a) { - PriorityQueue(queue: new_(), refs: dict.new()) -} - -pub fn insert( - queue: PriorityQueue(a), - value: a, - priority: Int, -) -> PriorityQueue(a) { - let ref = make_ref() - - let refs = - queue.refs - |> dict.insert(value, ref) - - PriorityQueue( - refs: refs, - queue: insert_(#(value, ref), priority, queue.queue), - ) -} - -pub fn pop(queue: PriorityQueue(a)) -> Result(#(a, PriorityQueue(a)), Nil) { - case pop_(queue.queue) { - #(Value(#(value, ref), _priority), pqueue) -> { - let assert Ok(recently_enqueued_ref) = dict.get(queue.refs, value) - case recently_enqueued_ref == ref { - True -> Ok(#(value, PriorityQueue(refs: queue.refs, queue: pqueue))) - False -> pop(PriorityQueue(refs: queue.refs, queue: pqueue)) - } - } - #(Empty, _pqueue) -> { - Error(Nil) - } - } -} diff --git a/aoc2023-gleam/test/aoc2023_test.gleam b/aoc2023-gleam/test/aoc2023_test.gleam deleted file mode 100644 index 2b696a4..0000000 --- a/aoc2023-gleam/test/aoc2023_test.gleam +++ /dev/null @@ -1,5 +0,0 @@ -import showtime - -pub fn main() { - showtime.main() -} diff --git a/aoc2023-gleam/test/day1/day1_test.gleam b/aoc2023-gleam/test/day1/day1_test.gleam deleted file mode 100644 index 374653c..0000000 --- a/aoc2023-gleam/test/day1/day1_test.gleam +++ /dev/null @@ -1,57 +0,0 @@ -import gleam/list -import showtime/tests/should -import adglent.{type Example, Example} -import day1/solve - -type Problem1AnswerType = - String - -type Problem2AnswerType = - String - -/// Add examples for part 1 here: -/// ```gleam -///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] -/// ``` -const part1_examples: List(Example(Problem1AnswerType)) = [ - Example( - "1abc2 -pqr3stu8vwx -a1b2c3d4e5f -treb7uchet", - "142", - ), -] - -/// Add examples for part 2 here: -/// ```gleam -///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] -/// ``` -const part2_examples: List(Example(Problem2AnswerType)) = [ - Example( - "two1nine -eightwothree -abcone2threexyz -xtwone3four -4nineeightseven2 -zoneight234 -7pqrstsixteen", - "281", - ), -] - -pub fn part1_test() { - part1_examples - |> should.not_equal([]) - use example <- list.map(part1_examples) - solve.part1(example.input) - |> should.equal(example.answer) -} - -pub fn part2_test() { - part2_examples - |> should.not_equal([]) - use example <- list.map(part2_examples) - solve.part2(example.input) - |> should.equal(example.answer) -} diff --git a/aoc2023-gleam/test/day10/day10_test.gleam b/aoc2023-gleam/test/day10/day10_test.gleam deleted file mode 100644 index be9d82e..0000000 --- a/aoc2023-gleam/test/day10/day10_test.gleam +++ /dev/null @@ -1,60 +0,0 @@ -import gleam/list -import showtime/tests/should -import adglent.{type Example, Example} -import day10/solve - -type Problem1AnswerType = - String - -type Problem2AnswerType = - String - -/// Add examples for part 1 here: -/// ```gleam -///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] -/// ``` -const part1_examples: List(Example(Problem1AnswerType)) = [ - Example( - "7-F7- -.FJ|7 -SJLL7 -|F--J -LJ.LJ", - "8", - ), -] - -/// Add examples for part 2 here: -/// ```gleam -///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] -/// ``` -const part2_examples: List(Example(Problem2AnswerType)) = [ - Example( - "........... -.S-------7. -.|F-----7|. -.||OOOOO||. -.||OOOOO||. -.|L-7OF-J|. -.|II|O|II|. -.L--JOL--J. -.....O.....", - "4", - ), -] - -pub fn part1_test() { - part1_examples - |> should.not_equal([]) - use example <- list.map(part1_examples) - solve.part1(example.input) - |> should.equal(example.answer) -} - -pub fn part2_test() { - part2_examples - |> should.not_equal([]) - use example <- list.map(part2_examples) - solve.part2(example.input) - |> should.equal(example.answer) -} diff --git a/aoc2023-gleam/test/day11/day11_test.gleam b/aoc2023-gleam/test/day11/day11_test.gleam deleted file mode 100644 index 8bb8c06..0000000 --- a/aoc2023-gleam/test/day11/day11_test.gleam +++ /dev/null @@ -1,66 +0,0 @@ -import gleam/list -import showtime/tests/should -import adglent.{type Example, Example} -import day11/solve - -type Problem1AnswerType = - String - -type Problem2AnswerType = - String - -/// Add examples for part 1 here: -/// ```gleam -///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] -/// ``` -const part1_examples: List(Example(Problem1AnswerType)) = [ - Example( - "...#...... -.......#.. -#......... -.......... -......#... -.#........ -.........# -.......... -.......#.. -#...#.....", - "374", - ), -] - -/// Add examples for part 2 here: -/// ```gleam -///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] -/// ``` -const part2_examples: List(Example(Problem2AnswerType)) = [ - Example( - "...#...... -.......#.. -#......... -.......... -......#... -.#........ -.........# -.......... -.......#.. -#...#.....", - "8410", - ), -] - -pub fn part1_test() { - part1_examples - |> should.not_equal([]) - use example <- list.map(part1_examples) - solve.part1(example.input) - |> should.equal(example.answer) -} - -pub fn part2_test() { - part2_examples - |> should.not_equal([]) - use example <- list.map(part2_examples) - solve.part2(example.input) - |> should.equal(example.answer) -} diff --git a/aoc2023-gleam/test/day12/day12_test.gleam b/aoc2023-gleam/test/day12/day12_test.gleam deleted file mode 100644 index 3daf0e9..0000000 --- a/aoc2023-gleam/test/day12/day12_test.gleam +++ /dev/null @@ -1,48 +0,0 @@ -import gleam/list -import showtime/tests/should -import adglent.{type Example, Example} -import day12/solve - -type Problem1AnswerType = - String - -type Problem2AnswerType = - String - -/// Add examples for part 1 here: -/// ```gleam -///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] -/// ``` -const part1_examples: List(Example(Problem1AnswerType)) = [ - Example( - "???.### 1,1,3 -.??..??...?##. 1,1,3 -?#?#?#?#?#?#?#? 1,3,1,6 -????.#...#... 4,1,1 -????.######..#####. 1,6,5 -?###???????? 3,2,1", - "21", - ), -] - -/// Add examples for part 2 here: -/// ```gleam -///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] -/// ``` -const part2_examples: List(Example(Problem2AnswerType)) = [] - -pub fn part1_test() { - part1_examples - |> should.not_equal([]) - use example <- list.map(part1_examples) - solve.part1(example.input) - |> should.equal(example.answer) -} - -pub fn part2_test() { - part2_examples - |> should.not_equal([]) - use example <- list.map(part2_examples) - solve.part2(example.input) - |> should.equal(example.answer) -} diff --git a/aoc2023-gleam/test/day13/day13_test.gleam b/aoc2023-gleam/test/day13/day13_test.gleam deleted file mode 100644 index 7c65bed..0000000 --- a/aoc2023-gleam/test/day13/day13_test.gleam +++ /dev/null @@ -1,76 +0,0 @@ -import gleam/list -import showtime/tests/should -import adglent.{type Example, Example} -import day13/solve - -type Problem1AnswerType = - String - -type Problem2AnswerType = - String - -/// Add examples for part 1 here: -/// ```gleam -///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] -/// ``` -const part1_examples: List(Example(Problem1AnswerType)) = [ - Example( - "#.##..##. -..#.##.#. -##......# -##......# -..#.##.#. -..##..##. -#.#.##.#. - -#...##..# -#....#..# -..##..### -#####.##. -#####.##. -..##..### -#....#..#", - "405", - ), -] - -/// Add examples for part 2 here: -/// ```gleam -///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] -/// ``` -const part2_examples: List(Example(Problem2AnswerType)) = [ - Example( - "#.##..##. -..#.##.#. -##......# -##......# -..#.##.#. -..##..##. -#.#.##.#. - -#...##..# -#....#..# -..##..### -#####.##. -#####.##. -..##..### -#....#..#", - "400", - ), -] - -pub fn part1_test() { - part1_examples - |> should.not_equal([]) - use example <- list.map(part1_examples) - solve.part1(example.input) - |> should.equal(example.answer) -} - -pub fn part2_test() { - part2_examples - |> should.not_equal([]) - use example <- list.map(part2_examples) - solve.part2(example.input) - |> should.equal(example.answer) -} diff --git a/aoc2023-gleam/test/day14/day14_test.gleam b/aoc2023-gleam/test/day14/day14_test.gleam deleted file mode 100644 index 8efa74e..0000000 --- a/aoc2023-gleam/test/day14/day14_test.gleam +++ /dev/null @@ -1,66 +0,0 @@ -import gleam/list -import showtime/tests/should -import adglent.{type Example, Example} -import day14/solve - -type Problem1AnswerType = - String - -type Problem2AnswerType = - String - -/// Add examples for part 1 here: -/// ```gleam -///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] -/// ``` -const part1_examples: List(Example(Problem1AnswerType)) = [ - Example( - "O....#.... -O.OO#....# -.....##... -OO.#O....O -.O.....O#. -O.#..O.#.# -..O..#O..O -.......O.. -#....###.. -#OO..#....", - "136", - ), -] - -/// Add examples for part 2 here: -/// ```gleam -///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] -/// ``` -const part2_examples: List(Example(Problem2AnswerType)) = [ - Example( - "O....#.... -O.OO#....# -.....##... -OO.#O....O -.O.....O#. -O.#..O.#.# -..O..#O..O -.......O.. -#....###.. -#OO..#....", - "64", - ), -] - -pub fn part1_test() { - part1_examples - |> should.not_equal([]) - use example <- list.map(part1_examples) - solve.part1(example.input) - |> should.equal(example.answer) -} - -pub fn part2_test() { - part2_examples - |> should.not_equal([]) - use example <- list.map(part2_examples) - solve.part2(example.input) - |> should.equal(example.answer) -} diff --git a/aoc2023-gleam/test/day15/day15_test.gleam b/aoc2023-gleam/test/day15/day15_test.gleam deleted file mode 100644 index 0ecaecc..0000000 --- a/aoc2023-gleam/test/day15/day15_test.gleam +++ /dev/null @@ -1,42 +0,0 @@ -import gleam/list -import showtime/tests/should -import adglent.{type Example, Example} -import day15/solve - -type Problem1AnswerType = - String - -type Problem2AnswerType = - String - -/// Add examples for part 1 here: -/// ```gleam -///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] -/// ``` -const part1_examples: List(Example(Problem1AnswerType)) = [ - Example("rn=1,cm-,qp=3,cm=2,qp-,pc=4,ot=9,ab=5,pc-,pc=6,ot=7", "1320"), -] - -/// Add examples for part 2 here: -/// ```gleam -///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] -/// ``` -const part2_examples: List(Example(Problem2AnswerType)) = [ - Example("rn=1,cm-,qp=3,cm=2,qp-,pc=4,ot=9,ab=5,pc-,pc=6,ot=7", "145"), -] - -pub fn part1_test() { - part1_examples - |> should.not_equal([]) - use example <- list.map(part1_examples) - solve.part1(example.input) - |> should.equal(example.answer) -} - -pub fn part2_test() { - part2_examples - |> should.not_equal([]) - use example <- list.map(part2_examples) - solve.part2(example.input) - |> should.equal(example.answer) -} diff --git a/aoc2023-gleam/test/day16/day16_test.gleam b/aoc2023-gleam/test/day16/day16_test.gleam deleted file mode 100644 index 036504e..0000000 --- a/aoc2023-gleam/test/day16/day16_test.gleam +++ /dev/null @@ -1,66 +0,0 @@ -import gleam/list -import showtime/tests/should -import adglent.{type Example, Example} -import day16/solve - -type Problem1AnswerType = - Int - -type Problem2AnswerType = - Int - -/// Add examples for part 1 here: -/// ```gleam -///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] -/// ``` -const part1_examples: List(Example(Problem1AnswerType)) = [ - Example( - ".|...\\.... -|.-.\\..... -.....|-... -........|. -.......... -.........\\ -..../.\\\\.. -.-.-/..|.. -.|....-|.\\ -..//.|....", - 46, - ), -] - -/// Add examples for part 2 here: -/// ```gleam -///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] -/// ``` -const part2_examples: List(Example(Problem2AnswerType)) = [ - Example( - ".|...\\.... -|.-.\\..... -.....|-... -........|. -.......... -.........\\ -..../.\\\\.. -.-.-/..|.. -.|....-|.\\ -..//.|....", - 51, - ), -] - -pub fn part1_test() { - part1_examples - |> should.not_equal([]) - use example <- list.map(part1_examples) - solve.part1(example.input) - |> should.equal(example.answer) -} - -pub fn part2_test() { - part2_examples - |> should.not_equal([]) - use example <- list.map(part2_examples) - solve.part2(example.input) - |> should.equal(example.answer) -} diff --git a/aoc2023-gleam/test/day17/day17_test.gleam b/aoc2023-gleam/test/day17/day17_test.gleam deleted file mode 100644 index 2ce48e2..0000000 --- a/aoc2023-gleam/test/day17/day17_test.gleam +++ /dev/null @@ -1,54 +0,0 @@ -import gleam/list -import showtime/tests/should -import adglent.{type Example, Example} -import day17/solve - -type Problem1AnswerType = - String - -// type Problem2AnswerType = -// String - -/// Add examples for part 1 here: -/// ```gleam -///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] -/// ``` -const part1_examples: List(Example(Problem1AnswerType)) = [ - Example( - "2413432311323 -3215453535623 -3255245654254 -3446585845452 -4546657867536 -1438598798454 -4457876987766 -3637877979653 -4654967986887 -4564679986453 -1224686865563 -2546548887735 -4322674655533", - "102", - ), -] - -// /// ``` -// const part2_examples: List(Example(Problem2AnswerType)) = [] - -/// Add examples for part 2 here: -/// ```gleam -///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] -pub fn part1_test() { - part1_examples - |> should.not_equal([]) - use example <- list.map(part1_examples) - solve.part1(example.input) - |> should.equal(example.answer) -} -// pub fn part2_test() { -// part2_examples -// |> should.not_equal([]) -// use example <- list.map(part2_examples) -// solve.part2(example.input) -// |> should.equal(example.answer) -// } diff --git a/aoc2023-gleam/test/day18/day18_test.gleam b/aoc2023-gleam/test/day18/day18_test.gleam deleted file mode 100644 index 7b510c8..0000000 --- a/aoc2023-gleam/test/day18/day18_test.gleam +++ /dev/null @@ -1,74 +0,0 @@ -import gleam/list -import showtime/tests/should -import adglent.{type Example, Example} -import day18/solve - -type Problem1AnswerType = - String - -type Problem2AnswerType = - String - -/// Add examples for part 1 here: -/// ```gleam -///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] -/// ``` -const part1_examples: List(Example(Problem1AnswerType)) = [ - Example( - "R 6 (#70c710) -D 5 (#0dc571) -L 2 (#5713f0) -D 2 (#d2c081) -R 2 (#59c680) -D 2 (#411b91) -L 5 (#8ceee2) -U 2 (#caa173) -L 1 (#1b58a2) -U 2 (#caa171) -R 2 (#7807d2) -U 3 (#a77fa3) -L 2 (#015232) -U 2 (#7a21e3)", - "62", - ), -] - -/// Add examples for part 2 here: -/// ```gleam -///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] -/// ``` -const part2_examples: List(Example(Problem2AnswerType)) = [ - Example( - "R 6 (#70c710) -D 5 (#0dc571) -L 2 (#5713f0) -D 2 (#d2c081) -R 2 (#59c680) -D 2 (#411b91) -L 5 (#8ceee2) -U 2 (#caa173) -L 1 (#1b58a2) -U 2 (#caa171) -R 2 (#7807d2) -U 3 (#a77fa3) -L 2 (#015232) -U 2 (#7a21e3)", - "952408144115", - ), -] - -pub fn part1_test() { - part1_examples - |> should.not_equal([]) - use example <- list.map(part1_examples) - solve.part1(example.input) - |> should.equal(example.answer) -} - -pub fn part2_test() { - part2_examples - |> should.not_equal([]) - use example <- list.map(part2_examples) - solve.part2(example.input) - |> should.equal(example.answer) -} diff --git a/aoc2023-gleam/test/day19/day19_test.gleam b/aoc2023-gleam/test/day19/day19_test.gleam deleted file mode 100644 index c911de5..0000000 --- a/aoc2023-gleam/test/day19/day19_test.gleam +++ /dev/null @@ -1,80 +0,0 @@ -import gleam/list -import showtime/tests/should -import adglent.{type Example, Example} -import day19/solve - -type Problem1AnswerType = - String - -type Problem2AnswerType = - String - -/// Add examples for part 1 here: -/// ```gleam -///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] -/// ``` -const part1_examples: List(Example(Problem1AnswerType)) = [ - Example( - "px{a<2006:qkq,m>2090:A,rfg} -pv{a>1716:R,A} -lnx{m>1548:A,A} -rfg{s<537:gd,x>2440:R,A} -qs{s>3448:A,lnx} -qkq{x<1416:A,crn} -crn{x>2662:A,R} -in{s<1351:px,qqz} -qqz{s>2770:qs,m<1801:hdj,R} -gd{a>3333:R,R} -hdj{m>838:A,pv} - -{x=787,m=2655,a=1222,s=2876} -{x=1679,m=44,a=2067,s=496} -{x=2036,m=264,a=79,s=2244} -{x=2461,m=1339,a=466,s=291} -{x=2127,m=1623,a=2188,s=1013}", - "19114", - ), -] - -/// Add examples for part 2 here: -/// ```gleam -///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] -/// ``` -const part2_examples: List(Example(Problem2AnswerType)) = [ - Example( - "px{a<2006:qkq,m>2090:A,rfg} -pv{a>1716:R,A} -lnx{m>1548:A,A} -rfg{s<537:gd,x>2440:R,A} -qs{s>3448:A,lnx} -qkq{x<1416:A,crn} -crn{x>2662:A,R} -in{s<1351:px,qqz} -qqz{s>2770:qs,m<1801:hdj,R} -gd{a>3333:R,R} -hdj{m>838:A,pv} - -{x=787,m=2655,a=1222,s=2876} -{x=1679,m=44,a=2067,s=496} -{x=2036,m=264,a=79,s=2244} -{x=2461,m=1339,a=466,s=291} -{x=2127,m=1623,a=2188,s=1013}", - "167409079868000", - ), -] - -pub fn part1_test() { - part1_examples - |> should.not_equal([]) - use example <- list.map(part1_examples) - solve.part1(example.input) - |> should.equal(example.answer) -} - -pub fn part2_test() { - part2_examples - |> should.not_equal([]) - use example <- list.map(part2_examples) - solve.part2(example.input) - |> should.equal(example.answer) -} diff --git a/aoc2023-gleam/test/day2/day2_test.gleam b/aoc2023-gleam/test/day2/day2_test.gleam deleted file mode 100644 index 28a65da..0000000 --- a/aoc2023-gleam/test/day2/day2_test.gleam +++ /dev/null @@ -1,57 +0,0 @@ -import gleam/list -import showtime/tests/should -import adglent.{type Example, Example} -import day2/solve - -type Problem1AnswerType = - Int - -type Problem2AnswerType = - Int - -/// Add examples for part 1 here: -/// ```gleam -///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] -/// ``` -const part1_examples: List(Example(Problem1AnswerType)) = [ - Example( - "Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green -Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue -Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red -Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red -Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green", - 8, - ), -] - -/// Add examples for part 2 here: -/// ```gleam -///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] -/// ``` -const part2_examples: List(Example(Problem2AnswerType)) = [ - Example( - "Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green -Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue -Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red -Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red -Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green", - 2286, - ), - Example("Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green", 48), -] - -pub fn part1_test() { - part1_examples - |> should.not_equal([]) - use example <- list.map(part1_examples) - solve.part1(example.input) - |> should.equal(example.answer) -} - -pub fn part2_test() { - part2_examples - |> should.not_equal([]) - use example <- list.map(part2_examples) - solve.part2(example.input) - |> should.equal(example.answer) -} diff --git a/aoc2023-gleam/test/day20/day20_test.gleam b/aoc2023-gleam/test/day20/day20_test.gleam deleted file mode 100644 index 9b79b05..0000000 --- a/aoc2023-gleam/test/day20/day20_test.gleam +++ /dev/null @@ -1,55 +0,0 @@ -import gleam/list -import showtime/tests/should -import adglent.{type Example, Example} -import day20/solve - -type Problem1AnswerType = - String - -type Problem2AnswerType = - String - -/// Add examples for part 1 here: -/// ```gleam -///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] -/// ``` -const part1_examples: List(Example(Problem1AnswerType)) = [ - Example( - "broadcaster -> a, b, c -%a -> b -%b -> c -%c -> inv -&inv -> a", - "32000000", - ), - Example( - "broadcaster -> a -%a -> inv, con -&inv -> b -%b -> con -&con -> output -output -> ", - "11687500", - ), -] - -// const part2_examples: List(Example(Problem2AnswerType)) = [] - -/// Add examples for part 2 here: -/// ```gleam -///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] -/// ``` -pub fn part1_test() { - part1_examples - |> should.not_equal([]) - use example <- list.map(part1_examples) - solve.part1(example.input) - |> should.equal(example.answer) -} -// pub fn part2_test() { -// part2_examples -// |> should.not_equal([]) -// use example <- list.map(part2_examples) -// solve.part2(example.input) -// |> should.equal(example.answer) -// } diff --git a/aoc2023-gleam/test/day21/day21_test.gleam b/aoc2023-gleam/test/day21/day21_test.gleam deleted file mode 100644 index 5f46808..0000000 --- a/aoc2023-gleam/test/day21/day21_test.gleam +++ /dev/null @@ -1,38 +0,0 @@ -import gleam/list -import showtime/tests/should -import adglent.{type Example, Example} -import day21/solve - -type Problem1AnswerType = - String - -type Problem2AnswerType = - String - -/// Add examples for part 1 here: -/// ```gleam -///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] -/// ``` -const part1_examples: List(Example(Problem1AnswerType)) = [] - -/// Add examples for part 2 here: -/// ```gleam -///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] -/// ``` -const part2_examples: List(Example(Problem2AnswerType)) = [] - -pub fn part1_test() { - part1_examples - |> should.not_equal([]) - use example <- list.map(part1_examples) - solve.part1(example.input) - |> should.equal(example.answer) -} - -pub fn part2_test() { - part2_examples - |> should.not_equal([]) - use example <- list.map(part2_examples) - solve.part2(example.input) - |> should.equal(example.answer) -} diff --git a/aoc2023-gleam/test/day22/day22_test.gleam b/aoc2023-gleam/test/day22/day22_test.gleam deleted file mode 100644 index 3f8c0ca..0000000 --- a/aoc2023-gleam/test/day22/day22_test.gleam +++ /dev/null @@ -1,60 +0,0 @@ -import gleam/list -import showtime/tests/should -import adglent.{type Example, Example} -import day22/solve - -type Problem1AnswerType = - Int - -type Problem2AnswerType = - Int - -/// Add examples for part 1 here: -/// ```gleam -///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] -/// ``` -const part1_examples: List(Example(Problem1AnswerType)) = [ - Example( - "1,0,1~1,2,1 -0,0,2~2,0,2 -0,2,3~2,2,3 -0,0,4~0,2,4 -2,0,5~2,2,5 -0,1,6~2,1,6 -1,1,8~1,1,9", - 5, - ), -] - -/// Add examples for part 2 here: -/// ```gleam -///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] -/// ``` -const part2_examples: List(Example(Problem2AnswerType)) = [ - Example( - "1,0,1~1,2,1 -0,0,2~2,0,2 -0,2,3~2,2,3 -0,0,4~0,2,4 -2,0,5~2,2,5 -0,1,6~2,1,6 -1,1,8~1,1,9", - 7, - ), -] - -pub fn part1_test() { - part1_examples - |> should.not_equal([]) - use example <- list.map(part1_examples) - solve.part1(example.input) - |> should.equal(example.answer) -} - -pub fn part2_test() { - part2_examples - |> should.not_equal([]) - use example <- list.map(part2_examples) - solve.part2(example.input) - |> should.equal(example.answer) -} diff --git a/aoc2023-gleam/test/day23/day23_test.gleam b/aoc2023-gleam/test/day23/day23_test.gleam deleted file mode 100644 index 206571c..0000000 --- a/aoc2023-gleam/test/day23/day23_test.gleam +++ /dev/null @@ -1,92 +0,0 @@ -import gleam/list -import showtime/tests/should -import adglent.{type Example, Example} -import day23/solve - -type Problem1AnswerType = - Int - -type Problem2AnswerType = - Int - -/// Add examples for part 1 here: -/// ```gleam -///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] -/// ``` -const part1_examples: List(Example(Problem1AnswerType)) = [ - Example( - "#.##################### -#.......#########...### -#######.#########.#.### -###.....#.>.>.###.#.### -###v#####.#v#.###.#.### -###.>...#.#.#.....#...# -###v###.#.#.#########.# -###...#.#.#.......#...# -#####.#.#.#######.#.### -#.....#.#.#.......#...# -#.#####.#.#.#########v# -#.#...#...#...###...>.# -#.#.#v#######v###.###v# -#...#.>.#...>.>.#.###.# -#####v#.#.###v#.#.###.# -#.....#...#...#.#.#...# -#.#########.###.#.#.### -#...###...#...#...#.### -###.###.#.###v#####v### -#...#...#.#.>.>.#.>.### -#.###.###.#.###.#.#v### -#.....###...###...#...# -#####################.#", - 94, - ), -] - -/// Add examples for part 2 here: -/// ```gleam -///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] -/// ``` -const part2_examples: List(Example(Problem2AnswerType)) = [ - Example( - "#.##################### -#.......#########...### -#######.#########.#.### -###.....#.>.>.###.#.### -###v#####.#v#.###.#.### -###.>...#.#.#.....#...# -###v###.#.#.#########.# -###...#.#.#.......#...# -#####.#.#.#######.#.### -#.....#.#.#.......#...# -#.#####.#.#.#########v# -#.#...#...#...###...>.# -#.#.#v#######v###.###v# -#...#.>.#...>.>.#.###.# -#####v#.#.###v#.#.###.# -#.....#...#...#.#.#...# -#.#########.###.#.#.### -#...###...#...#...#.### -###.###.#.###v#####v### -#...#...#.#.>.>.#.>.### -#.###.###.#.###.#.#v### -#.....###...###...#...# -#####################.#", - 154, - ), -] - -pub fn part1_test() { - part1_examples - |> should.not_equal([]) - use example <- list.map(part1_examples) - solve.part1(example.input) - |> should.equal(example.answer) -} - -pub fn part2_test() { - part2_examples - |> should.not_equal([]) - use example <- list.map(part2_examples) - solve.part2(example.input) - |> should.equal(example.answer) -} diff --git a/aoc2023-gleam/test/day3/day3_test.gleam b/aoc2023-gleam/test/day3/day3_test.gleam deleted file mode 100644 index 30e17a9..0000000 --- a/aoc2023-gleam/test/day3/day3_test.gleam +++ /dev/null @@ -1,66 +0,0 @@ -import gleam/list -import showtime/tests/should -import adglent.{type Example, Example} -import day3/solve - -type Problem1AnswerType = - Int - -type Problem2AnswerType = - Int - -/// Add examples for part 1 here: -/// ```gleam -///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] -/// ``` -const part1_examples: List(Example(Problem1AnswerType)) = [ - Example( - "467..114.. -...*...... -..35..633. -......#... -617*...... -.....+.58. -..592..... -......755. -...$.*.... -.664.598..", - 4361, - ), -] - -/// Add examples for part 2 here: -/// ```gleam -///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] -/// ``` -const part2_examples: List(Example(Problem2AnswerType)) = [ - Example( - "467..114.. -...*...... -..35..633. -......#... -617*...... -.....+.58. -..592..... -......755. -...$.*.... -.664.598..", - 467_835, - ), -] - -pub fn part1_test() { - part1_examples - |> should.not_equal([]) - use example <- list.map(part1_examples) - solve.part1(example.input) - |> should.equal(example.answer) -} - -pub fn part2_test() { - part2_examples - |> should.not_equal([]) - use example <- list.map(part2_examples) - solve.part2(example.input) - |> should.equal(example.answer) -} diff --git a/aoc2023-gleam/test/day4/day4_test.gleam b/aoc2023-gleam/test/day4/day4_test.gleam deleted file mode 100644 index 324fe36..0000000 --- a/aoc2023-gleam/test/day4/day4_test.gleam +++ /dev/null @@ -1,58 +0,0 @@ -import gleam/list -import showtime/tests/should -import adglent.{type Example, Example} -import day4/solve - -type Problem1AnswerType = - Int - -type Problem2AnswerType = - Int - -/// Add examples for part 1 here: -/// ```gleam -///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] -/// ``` -const part1_examples: List(Example(Problem1AnswerType)) = [ - Example( - "Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53 -Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19 -Card 3: 1 21 53 59 44 | 69 82 63 72 16 21 14 1 -Card 4: 41 92 73 84 69 | 59 84 76 51 58 5 54 83 -Card 5: 87 83 26 28 32 | 88 30 70 12 93 22 82 36 -Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11", - 13, - ), -] - -/// Add examples for part 2 here: -/// ```gleam -///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] -/// ``` -const part2_examples: List(Example(Problem2AnswerType)) = [ - Example( - "Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53 -Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19 -Card 3: 1 21 53 59 44 | 69 82 63 72 16 21 14 1 -Card 4: 41 92 73 84 69 | 59 84 76 51 58 5 54 83 -Card 5: 87 83 26 28 32 | 88 30 70 12 93 22 82 36 -Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11", - 30, - ), -] - -pub fn part1_test() { - part1_examples - |> should.not_equal([]) - use example <- list.map(part1_examples) - solve.part1(example.input) - |> should.equal(example.answer) -} - -pub fn part2_test() { - part2_examples - |> should.not_equal([]) - use example <- list.map(part2_examples) - solve.part2(example.input) - |> should.equal(example.answer) -} diff --git a/aoc2023-gleam/test/day5/day5_test.gleam b/aoc2023-gleam/test/day5/day5_test.gleam deleted file mode 100644 index 86a8692..0000000 --- a/aoc2023-gleam/test/day5/day5_test.gleam +++ /dev/null @@ -1,112 +0,0 @@ -import gleam/list -import showtime/tests/should -import adglent.{type Example, Example} -import day5/solve - -type Problem1AnswerType = - String - -type Problem2AnswerType = - String - -/// Add examples for part 1 here: -/// ```gleam -///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] -/// ``` -const part1_examples: List(Example(Problem1AnswerType)) = [ - Example( - "seeds: 79 14 55 13 - -seed-to-soil map: -50 98 2 -52 50 48 - -soil-to-fertilizer map: -0 15 37 -37 52 2 -39 0 15 - -fertilizer-to-water map: -49 53 8 -0 11 42 -42 0 7 -57 7 4 - -water-to-light map: -88 18 7 -18 25 70 - -light-to-temperature map: -45 77 23 -81 45 19 -68 64 13 - -temperature-to-humidity map: -0 69 1 -1 0 69 - -humidity-to-location map: -60 56 37 -56 93 4", - "35", - ), -] - -/// Add examples for part 2 here: -/// ```gleam -///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] -/// ``` -const part2_examples: List(Example(Problem2AnswerType)) = [ - Example( - "seeds: 79 14 55 13 - -seed-to-soil map: -50 98 2 -52 50 48 - -soil-to-fertilizer map: -0 15 37 -37 52 2 -39 0 15 - -fertilizer-to-water map: -49 53 8 -0 11 42 -42 0 7 -57 7 4 - -water-to-light map: -88 18 7 -18 25 70 - -light-to-temperature map: -45 77 23 -81 45 19 -68 64 13 - -temperature-to-humidity map: -0 69 1 -1 0 69 - -humidity-to-location map: -60 56 37 -56 93 4", - "46", - ), -] - -pub fn part1_test() { - part1_examples - |> should.not_equal([]) - use example <- list.map(part1_examples) - solve.part1(example.input) - |> should.equal(example.answer) -} - -pub fn part2_test() { - part2_examples - |> should.not_equal([]) - use example <- list.map(part2_examples) - solve.part2(example.input) - |> should.equal(example.answer) -} diff --git a/aoc2023-gleam/test/day6/day6_test.gleam b/aoc2023-gleam/test/day6/day6_test.gleam deleted file mode 100644 index c551993..0000000 --- a/aoc2023-gleam/test/day6/day6_test.gleam +++ /dev/null @@ -1,50 +0,0 @@ -import gleam/list -import showtime/tests/should -import adglent.{type Example, Example} -import day6/solve - -type Problem1AnswerType = - String - -type Problem2AnswerType = - String - -/// Add examples for part 1 here: -/// ```gleam -///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] -/// ``` -const part1_examples: List(Example(Problem1AnswerType)) = [ - Example( - "Time: 7 15 30 -Distance: 9 40 200", - "288", - ), -] - -/// Add examples for part 2 here: -/// ```gleam -///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] -/// ``` -const part2_examples: List(Example(Problem2AnswerType)) = [ - Example( - "Time: 7 15 30 -Distance: 9 40 200", - "71503", - ), -] - -pub fn part1_test() { - part1_examples - |> should.not_equal([]) - use example <- list.map(part1_examples) - solve.part1(example.input) - |> should.equal(example.answer) -} - -pub fn part2_test() { - part2_examples - |> should.not_equal([]) - use example <- list.map(part2_examples) - solve.part2(example.input) - |> should.equal(example.answer) -} diff --git a/aoc2023-gleam/test/day7/day7_test.gleam b/aoc2023-gleam/test/day7/day7_test.gleam deleted file mode 100644 index f7f8454..0000000 --- a/aoc2023-gleam/test/day7/day7_test.gleam +++ /dev/null @@ -1,56 +0,0 @@ -import gleam/list -import showtime/tests/should -import adglent.{type Example, Example} -import day7/solve - -type Problem1AnswerType = - String - -type Problem2AnswerType = - String - -/// Add examples for part 1 here: -/// ```gleam -///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] -/// ``` -const part1_examples: List(Example(Problem1AnswerType)) = [ - Example( - "32T3K 765 -T55J5 684 -KK677 28 -KTJJT 220 -QQQJA 483", - "6440", - ), -] - -/// Add examples for part 2 here: -/// ```gleam -///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] -/// ``` -const part2_examples: List(Example(Problem2AnswerType)) = [ - Example( - "32T3K 765 -T55J5 684 -KK677 28 -KTJJT 220 -QQQJA 483", - "5905", - ), -] - -pub fn part1_test() { - part1_examples - |> should.not_equal([]) - use example <- list.map(part1_examples) - solve.part1(example.input) - |> should.equal(example.answer) -} - -pub fn part2_test() { - part2_examples - |> should.not_equal([]) - use example <- list.map(part2_examples) - solve.part2(example.input) - |> should.equal(example.answer) -} diff --git a/aoc2023-gleam/test/day8/day8_test.gleam b/aoc2023-gleam/test/day8/day8_test.gleam deleted file mode 100644 index 2cd499a..0000000 --- a/aoc2023-gleam/test/day8/day8_test.gleam +++ /dev/null @@ -1,61 +0,0 @@ -import gleam/list -import showtime/tests/should -import adglent.{type Example, Example} -import day8/solve - -type Problem1AnswerType = - Int - -type Problem2AnswerType = - Int - -/// Add examples for part 1 here: -/// ```gleam -///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] -/// ``` -const part1_examples: List(Example(Problem1AnswerType)) = [ - Example( - "LLR - -AAA = (BBB, BBB) -BBB = (AAA, ZZZ) -ZZZ = (ZZZ, ZZZ)", - 6, - ), -] - -/// Add examples for part 2 here: -/// ```gleam -///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] -/// ``` -const part2_examples: List(Example(Problem2AnswerType)) = [ - Example( - "LR - -11A = (11B, XXX) -11B = (XXX, 11Z) -11Z = (11B, XXX) -22A = (22B, XXX) -22B = (22C, 22C) -22C = (22Z, 22Z) -22Z = (22B, 22B) -XXX = (XXX, XXX)", - 6, - ), -] - -pub fn part1_test() { - part1_examples - |> should.not_equal([]) - use example <- list.map(part1_examples) - solve.part1(example.input) - |> should.equal(example.answer) -} - -pub fn part2_test() { - part2_examples - |> should.not_equal([]) - use example <- list.map(part2_examples) - solve.part2(example.input) - |> should.equal(example.answer) -} diff --git a/aoc2023-gleam/test/day9/day9_test.gleam b/aoc2023-gleam/test/day9/day9_test.gleam deleted file mode 100644 index 84fd3ba..0000000 --- a/aoc2023-gleam/test/day9/day9_test.gleam +++ /dev/null @@ -1,52 +0,0 @@ -import gleam/list -import showtime/tests/should -import adglent.{type Example, Example} -import day9/solve - -type Problem1AnswerType = - String - -type Problem2AnswerType = - String - -/// Add examples for part 1 here: -/// ```gleam -///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] -/// ``` -const part1_examples: List(Example(Problem1AnswerType)) = [ - Example( - "0 3 6 9 12 15 -1 3 6 10 15 21 -10 13 16 21 30 45", - "114", - ), -] - -/// Add examples for part 2 here: -/// ```gleam -///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] -/// ``` -const part2_examples: List(Example(Problem2AnswerType)) = [ - Example( - "0 3 6 9 12 15 -1 3 6 10 15 21 -10 13 16 21 30 45", - "2", - ), -] - -pub fn part1_test() { - part1_examples - |> should.not_equal([]) - use example <- list.map(part1_examples) - solve.part1(example.input) - |> should.equal(example.answer) -} - -pub fn part2_test() { - part2_examples - |> should.not_equal([]) - use example <- list.map(part2_examples) - solve.part2(example.input) - |> should.equal(example.answer) -} diff --git a/aoc2023-racket/day-01/day-01.rkt b/aoc2023-racket/day-01/day-01.rkt deleted file mode 100644 index b720f79..0000000 --- a/aoc2023-racket/day-01/day-01.rkt +++ /dev/null @@ -1,38 +0,0 @@ -#lang racket - -(require advent-of-code - threading) - -(define calibration-values (fetch-aoc-input (find-session) 2023 1)) - -(define (to-number str) - (match (string->number str) - [#false (hash-ref word-to-digit str)] - [n n])) - -(define (parse-calibration-value v valid) - (for/fold ([acc '()] [value v] #:result (+ (to-number (first acc)) (* 10 (to-number (last acc))))) - ([_ (in-naturals)]) - #:break (equal? value "") - (let ([possible-prefix (findf (curry string-prefix? value) valid)]) - (if possible-prefix - (values (cons possible-prefix acc) (substring value 1)) - (values acc (substring value 1)))))) - -(define (total-calibration input valid) - (~> input - (string-trim) - (string-split "\n") - (map (λ~> (parse-calibration-value valid)) _) - (apply + _))) - -;; part 1 - -(define valid-for-part-1 (~> (range 1 10) (map ~a _))) -(total-calibration calibration-values valid-for-part-1) - -;; part 2 -(define word-to-digit - (hash "one" 1 "two" 2 "three" 3 "four" 4 "five" 5 "six" 6 "seven" 7 "eight" 8 "nine" 9)) -(define valid-for-part-2 (append valid-for-part-1 (hash-keys word-to-digit))) -(total-calibration calibration-values valid-for-part-2) diff --git a/aoc2023-racket/day-02/day-02-parser.rkt b/aoc2023-racket/day-02/day-02-parser.rkt deleted file mode 100644 index 76cc24f..0000000 --- a/aoc2023-racket/day-02/day-02-parser.rkt +++ /dev/null @@ -1,55 +0,0 @@ -#lang racket - -(require racket/hash - advent-of-code - data/applicative - data/either - data/monad - megaparsack - megaparsack/text - threading) - -(struct game (id r g b)) - -(define cube/p - (do [n <- integer/p] - space/p - [c <- (or/p (string/p "red") - (string/p "blue") - (string/p "green"))] - (pure (cons c n)))) - -(define draw/p - (do [xs <- (many/p cube/p #:min 1 #:max 3 #:sep (string/p ", "))] - (pure (apply hash (flatten xs))))) - -(define all-draws/p - (do (string/p "Game ") - [id <- integer/p] - (string/p ": ") - [all-draws <- (many/p draw/p #:min 1 #:sep (string/p "; "))] - (define maxima - (foldl (curry hash-union #:combine max) - (hash "red" 0 "green" 0 "blue" 0) - all-draws)) - (pure (game id - (hash-ref maxima "red") - (hash-ref maxima "green") - (hash-ref maxima "blue"))))) - -(define game-maxima - (~>> (open-aoc-input (find-session) 2023 2) - port->lines - (map (λ~>> (parse-string all-draws/p) - from-either)))) - -;; part 1 -(for/sum ([m (in-list game-maxima)] - #:unless (or (> (game-r m) 12) - (> (game-g m) 13) - (> (game-b m) 14))) - (game-id m)) - -;; part 2 -(for/sum ([m (in-list game-maxima)]) - (* (game-r m) (game-g m) (game-b m))) diff --git a/aoc2023-racket/day-02/day-02.rkt b/aoc2023-racket/day-02/day-02.rkt deleted file mode 100644 index 973d20c..0000000 --- a/aoc2023-racket/day-02/day-02.rkt +++ /dev/null @@ -1,35 +0,0 @@ -#lang racket - -(require advent-of-code) - -(struct roll (id red green blue)) - -(define all-games - (for/list ([raw-game (in-list (port->lines (open-aoc-input (find-session) 2023 2)))] - #:do [(define game (string-trim raw-game "Game ")) - (match-define (list id trials) (string-split game ": "))]) - (for/list ([trial (in-list (string-split trials "; "))]) - (for/fold ([acc (roll (string->number id) 0 0 0)]) - ([color (in-list (string-split trial ", "))]) - (match (string-split color) - [(list (app string->number n) "red") (struct-copy roll acc [red n])] - [(list (app string->number n) "green") (struct-copy roll acc [green n])] - [(list (app string->number n) "blue") (struct-copy roll acc [blue n])]))))) - -;; part 1 -(for/sum ([game (in-list all-games)] - #:when (andmap (λ (g) (and ((roll-red g) . <= . 12) - ((roll-green g) . <= . 13) - ((roll-blue g) . <= . 14))) - game)) - (roll-id (first game))) - -;; part 2 -(for/sum ([game (in-list all-games)]) - (define max-cubes - (for/fold ([acc (roll #f 0 0 0)]) ([r (in-list game)]) - (roll #f - (max (roll-red acc) (roll-red r)) - (max (roll-green acc) (roll-green r)) - (max (roll-blue acc) (roll-blue r))))) - (* (roll-red max-cubes) (roll-green max-cubes) (roll-blue max-cubes))) diff --git a/aoc2023-racket/day-03/day-03.rkt b/aoc2023-racket/day-03/day-03.rkt deleted file mode 100644 index 60e81a6..0000000 --- a/aoc2023-racket/day-03/day-03.rkt +++ /dev/null @@ -1,72 +0,0 @@ -#lang racket - -(require advent-of-code - threading) - -(struct posn (x y) #:transparent) -(struct part (n posns) #:transparent) - -(define (make-board port) - (for*/hash ([(row y) (in-indexed (port->lines port))] - [(col x) (in-indexed (string->list row))] - #:unless (equal? col #\.)) - (define v - (cond - [(string->number (string col))] - [(equal? col #\*) 'gear] - [else 'other])) - (values (posn x y) v))) - -(define board (~> (open-aoc-input (find-session) 2023 3 #:cache #true) make-board)) - -(define (posn (for/hash ([(k v) (in-hash b)] #:when (f v)) - (values k v)) - hash->list - (sort posn> part-posns (append-map neighbors) remove-duplicates)) -(define (symbol-in-neighbors b pt acc) - (~>> pt - to-neighbors - (ormap (λ (p) (let ([lookup (hash-ref b p #f)]) - (or (equal? lookup 'gear) (equal? lookup 'other))))) - ((λ (bool) (if bool (+ acc (part-n pt)) acc))))) - -;; part 1 -(define parts (~>> board (find-cells integer?) group-into-parts)) -(foldl (curry symbol-in-neighbors board) 0 parts) - -;; part 2 -(define gears (~>> board (find-cells (curry equal? 'gear)) (map car))) -(define parts-with-neighbors (map (λ (pt) (struct-copy part pt [posns (to-neighbors pt)])) parts)) - -(define (find-parts-near-gear pts gear) - (filter-map (λ (pt) (and (findf (curry equal? gear) (part-posns pt)) (part-n pt))) pts)) - -(~>> gears - (filter-map (λ~>> (find-parts-near-gear parts-with-neighbors) - ((λ (ns) (if (= (length ns) 2) (* (first ns) (second ns)) #f))))) - (apply +)) diff --git a/aoc2023-racket/day-04/day-04.rkt b/aoc2023-racket/day-04/day-04.rkt deleted file mode 100644 index 7a357c5..0000000 --- a/aoc2023-racket/day-04/day-04.rkt +++ /dev/null @@ -1,40 +0,0 @@ -#lang racket - -(require advent-of-code - data/applicative - data/either - data/monad - megaparsack - megaparsack/text - threading) - -(struct card (n wins)) - -(define card/p - (do (string/p "Card") - (many/p space/p) - [n <- integer/p] - (string/p ":") - (many/p space/p) - [winners <- (many-until/p integer/p #:sep (many/p space/p) #:end (try/p (string/p " | ")))] - (many/p space/p) - [has <- (many+/p integer/p #:sep (many/p space/p))] - (pure (card n (set-count (set-intersect (first winners) has)))))) - -(define raw-cards (~> (open-aoc-input (find-session) 2023 4 #:cache #true) port->lines)) - -;; part 1 -(for/sum ([raw-card (in-list raw-cards)] - #:do [(match-define (success (card _ wins)) (parse-string card/p raw-card))] - #:unless (= wins 0)) - (expt 2 (sub1 wins))) - -;; part 2 -(for/fold ([counts (for/hash ([n (in-inclusive-range 1 (length raw-cards))]) - (values n 1))] - #:result (apply + (hash-values counts))) - ([raw-card (in-list raw-cards)] - #:do [(match-define (success (card n wins)) (parse-string card/p raw-card))]) - (define bonus-range (inclusive-range (+ n 1) (+ n wins))) - (define won-cards (hash-ref counts n)) - (foldl (λ (n acc) (hash-update acc n (curry + won-cards))) counts bonus-range)) diff --git a/aoc2023-racket/day-05/day-05.rkt b/aoc2023-racket/day-05/day-05.rkt deleted file mode 100644 index 5b9aa52..0000000 --- a/aoc2023-racket/day-05/day-05.rkt +++ /dev/null @@ -1,91 +0,0 @@ -#lang racket - -(require advent-of-code - algorithms - threading) - -(struct map-range (start end offset)) -(struct seed-range (start end)) - -(define input (fetch-aoc-input (find-session) 2023 5 #:cache #true)) - -(match-define (list* raw-seeds raw-mappings) (string-split input "\n\n")) - -(define seeds-naive (~> raw-seeds (string-split " ") rest (map string->number _))) -(define mappers - (for/list ([raw-mapping (in-list raw-mappings)]) - (for/lists (map-ranges #:result (sort map-ranges < #:key map-range-start)) - ([raw-map-range (in-list (rest (string-split raw-mapping "\n")))] - #:do [(match-define (list dest source width) - (~> raw-map-range (string-split _ " ") (map string->number _)))]) - (map-range source (+ source width -1) (- dest source))))) - -;; part 1 -(define (in-map-range? n mr) - (<= (map-range-start mr) n (map-range-end mr))) - -(define (transform-value mapper n) - (match mapper - ['() n] - [(list* mr _) - #:when (in-map-range? n mr) - (+ n (map-range-offset mr))] - [(list* _ rest-mapper) (transform-value rest-mapper n)])) - -(for/lists (transforms #:result (apply min transforms)) - ([seed (in-list seeds-naive)]) - (foldl transform-value seed mappers)) - -;; part 2 -(define (remap-range r mapper [acc '()]) - (match-define (seed-range r-start r-end) r) - (match mapper - ; mapper exhausted - ['() (cons r acc)] - ; range to the left - not covered by this mapping, so keep as-is - [(list* (map-range m-start _ _) _) - #:when (< r-end m-start) - (cons r acc)] - ; range to the right - move to next map-range - [(list* (map-range _ m-end _) ms) - #:when (< m-end r-start) - (remap-range r ms acc)] - ; range is inside map-range - transform whole range - [(list* (map-range m-start m-end offset) _) - #:when (and (<= m-start r-start) (<= r-end m-end)) - (cons (seed-range (+ r-start offset) (+ r-end offset)) acc)] - ; range overlaps start only - keep left side, transform right side - [(list* (map-range m-start m-end offset) ms) - #:when (and (< r-start m-start) (<= r-end m-end)) - (remap-range (seed-range (add1 m-end) r-end) - ms - (cons (seed-range (+ m-start offset) (+ r-end offset)) acc))] - ; range overlaps end - transform left side, pass right side - [(list* (map-range m-start m-end offset) ms) - #:when (and (< m-start r-start) (<= m-end r-end)) - (remap-range (seed-range (add1 m-end) r-end) - ms - (cons (seed-range (+ r-start offset) (+ m-end offset)) acc))] - ; range overlaps whole map-range - keep left side, transform middle, pass right side - [(list* (map-range m-start m-end offset) ms) - (remap-range (seed-range (add1 m-end) r-end) - ms - (cons (seed-range (+ m-start offset) (+ m-end offset)) - (cons (seed-range (add1 m-end) r-end) acc)))])) - -(define (remap-ranges rs mappers) - (cond - [(empty? mappers) rs] - [else - (~>> rs (append-map (curryr remap-range (first mappers))) (remap-ranges _ (rest mappers)))])) - -(~> seeds-naive - (chunks-of 2) - (map (λ (xs) - (match-define (list start width) xs) - (~> (list (seed-range start (+ start width -1))) - (remap-ranges mappers) - (argmin seed-range-start _))) - _) - (argmin seed-range-start _) - seed-range-start) diff --git a/aoc2023-racket/day-06/day-06.rkt b/aoc2023-racket/day-06/day-06.rkt deleted file mode 100644 index 53ca9ee..0000000 --- a/aoc2023-racket/day-06/day-06.rkt +++ /dev/null @@ -1,32 +0,0 @@ -#lang racket - -(require advent-of-code - threading) - -(match-define (list times distances) - (~> (open-aoc-input (find-session) 2023 6 #:cache #true) port->lines)) - -;; part 1 -(define get-numbers (λ~> string-split (map string->number _) rest)) -(define (find-bound race-time dist button-time step) - (if (< dist (* button-time (- race-time button-time))) - button-time - (find-bound race-time dist (+ step button-time) step))) - -(define (lower-bound rtime dist) - (find-bound rtime dist 1 1)) -(define (upper-bound rtime dist) - (find-bound rtime dist rtime -1)) - -(for/fold ([acc 1]) - ([race-time (in-list (get-numbers times))] - [distance (in-list (get-numbers distances))]) - (* acc (add1 (- (upper-bound race-time distance) (lower-bound race-time distance))))) - -;; part 2 - -(define unkern (λ~>> get-numbers (apply ~a) string->number)) -(define big-time (unkern times)) -(define big-distance (unkern distances)) - -(add1 (- (upper-bound big-time big-distance) (lower-bound big-time big-distance))) diff --git a/aoc2023-racket/day-07/day-07.rkt b/aoc2023-racket/day-07/day-07.rkt deleted file mode 100644 index 30e629b..0000000 --- a/aoc2023-racket/day-07/day-07.rkt +++ /dev/null @@ -1,82 +0,0 @@ -#lang racket - -(require advent-of-code - threading - memo) - -(struct hand (cards wager)) - -(define/match (card->int card) - [((? char-numeric?)) (~> card string string->number)] - [(#\A) 14] - [(#\K) 13] - [(#\Q) 12] - [(#\J) 11] - [(#\T) 10] - [(#\*) 1]) - -(define (parse-hand str #:jokers [jokers? #f]) - (match-define (list card-str wager-str) (string-split str)) - (define cards - (~> card-str - ((λ (str) (if jokers? (string-replace str "J" "*") str))) - string->list - (map card->int _))) - (define wager (~> wager-str string->number)) - (hand cards wager)) - -(define input (~> (open-aoc-input (find-session) 2023 7 #:cache #true) port->lines)) - -(define/memoize (identify-hand h) - (define freqs (~> h hand-cards (sort <) (group-by identity _) (map length _))) - (match freqs - [(list-no-order 5) 8] - [(list-no-order 1 4) 7] - [(list-no-order 2 3) 6] - [(list-no-order 1 1 3) 5] - [(list-no-order 1 2 2) 4] - [(list-no-order 1 1 1 2) 3] - [(list-no-order 1 1 1 1 1) 2] - [_ 1])) - -(define (compare-first-card cs1 cs2) - (if (= (first cs1) (first cs2)) - (compare-first-card (rest cs1) (rest cs2)) - (< (first cs1) (first cs2)))) - -(define (compare-hands with h1 h2) - (define rank1 (with h1)) - (define rank2 (with h2)) - (if (= rank1 rank2) (compare-first-card (hand-cards h1) (hand-cards h2)) (< rank1 rank2))) - -;; part 1 - -(define (compare-hands-no-wilds h1 h2) - (compare-hands identify-hand h1 h2)) - -(define (total-score in #:jokers [jokers? #false]) - (define sorted-hands - (~> in - (map (curry parse-hand #:jokers jokers?) _) - (sort (if jokers? compare-hands-no-wilds compare-hands-with-wilds)))) - (for/sum ([(h i) - (in-indexed sorted-hands)]) - (* (add1 i) (hand-wager h)))) - -(total-score input) - -;; part 2 - -(define/memoize (find-best-joker-substitution h) - (for/fold ([best-hand (hand '() 0)]) - ([wild (in-inclusive-range 2 14)]) - (define trial-hand - (hand (map (λ (c) (if (= c 1) wild c)) (hand-cards h)) (hand-wager h))) - (if (> (identify-hand trial-hand) (identify-hand best-hand)) - trial-hand - best-hand))) - -(define (compare-hands-with-wilds h1 h2) - (compare-hands (λ~> find-best-joker-substitution identify-hand) h1 h2)) - -(total-score input #:jokers #true) diff --git a/aoc2023-racket/day-08/day-08.rkt b/aoc2023-racket/day-08/day-08.rkt deleted file mode 100644 index 06daafa..0000000 --- a/aoc2023-racket/day-08/day-08.rkt +++ /dev/null @@ -1,36 +0,0 @@ -#lang racket - -(require advent-of-code - threading) - -(struct exits (left right) #:transparent) - -(match-define (list raw-directions raw-maze) - (~> (fetch-aoc-input (find-session) 2023 8 #:cache #true) (string-split "\n\n"))) - -(define directions (string->list raw-directions)) - -(define maze - (for/hash ([line (in-list (string-split raw-maze "\n"))]) - (match (regexp-match #rx"(...) = \\((...), (...)\\)" line) - [(list _ name left right) (values name (exits left right))]))) - -(define (to-next-node start end dirs maze) - (for/fold ([current start] - [acc 0] - #:result acc) - ([dir (in-cycle dirs)]) - #:break (string-suffix? current end) - (define node (hash-ref maze current)) - (case dir - [(#\L) (values (exits-left node) (add1 acc))] - [(#\R) (values (exits-right node) (add1 acc))]))) - -;; part 1 -(to-next-node "AAA" "ZZZ" directions maze) - -;; part 2 -(for/lists (ns #:result (apply lcm ns)) - ([start (in-list (hash-keys maze))] - #:when (string-suffix? start "A")) - (to-next-node start "Z" directions maze)) diff --git a/aoc2023-racket/day-09/day-09-polynomial.rkt b/aoc2023-racket/day-09/day-09-polynomial.rkt deleted file mode 100644 index 5bacb1f..0000000 --- a/aoc2023-racket/day-09/day-09-polynomial.rkt +++ /dev/null @@ -1,17 +0,0 @@ -#lang racket - -(require advent-of-code - threading - simple-polynomial/tools) - -(define histories - (for/list ([raw-history (in-lines (open-aoc-input (find-session) 2023 9 #:cache #true))]) - (~>> raw-history - string-split - (map string->number)))) - -(for/lists (left right #:result (cons (apply + left) (apply + right))) - ([history (in-list histories)]) - (define f (interpolate-at-integer-points history)) - (values (f -1) - (f (length history)))) diff --git a/aoc2023-racket/day-09/day-09.rkt b/aoc2023-racket/day-09/day-09.rkt deleted file mode 100644 index 5eda1eb..0000000 --- a/aoc2023-racket/day-09/day-09.rkt +++ /dev/null @@ -1,32 +0,0 @@ -#lang racket - -(require advent-of-code - threading) - -(define histories - (for/list ([raw-history (in-lines (open-aoc-input (find-session) 2023 9 #:cache #true))]) - (~>> raw-history - string-split - (map string->number)))) - -(define (constant? xs) - (= 1 (length (remove-duplicates xs)))) - -(define/match (derivative xs) - [((list a b)) (list (- b a))] - [((list* a b _)) (cons (- b a) (derivative (rest xs)))]) - -(define (extrapolate xs) - (if (constant? xs) - (car xs) - (+ (last xs) (extrapolate (derivative xs))))) - -;; part 1 -(~>> histories - (map extrapolate) - (apply +)) - -;; part 2 -(~>> histories - (map (λ~> reverse extrapolate)) - (apply +)) diff --git a/aoc2023-racket/day-10/day-10.rkt b/aoc2023-racket/day-10/day-10.rkt deleted file mode 100644 index 64d8727..0000000 --- a/aoc2023-racket/day-10/day-10.rkt +++ /dev/null @@ -1,97 +0,0 @@ -#lang racket - -(require advent-of-code - threading) - -(struct posn (r c) #:transparent) - -(define/match (add-posns _p1 _p2) - [((posn x1 y1) (posn x2 y2)) (posn (+ x1 x2) (+ y1 y2))]) - -(define go-north (posn -1 0)) -(define go-south (posn 1 0)) -(define go-east (posn 0 1)) -(define go-west (posn 0 -1)) - -(define initial-directions - (list (cons go-north '(#\| #\7 #\F)) - (cons go-south '(#\| #\J #\L)) - (cons go-east '(#\- #\J #\7)) - (cons go-west '(#\- #\F #\L)))) - -(define/match (pipe-neighbors _pipe) - [(#\|) (list go-north go-south)] - [(#\-) (list go-east go-west)] - [(#\L) (list go-north go-east)] - [(#\F) (list go-south go-east)] - [(#\7) (list go-south go-west)] - [(#\J) (list go-north go-west)]) - -(define (make-pipe-grid in) - (for*/hash ([(row r) (in-indexed (string-split in "\n"))] - [(ch c) (in-indexed (string->list row))]) - (values (posn (add1 r) (add1 c)) ch))) - -(define (get-valid-S-neighbors S grid) - (for/list ([dir (in-list initial-directions)] - #:do [(match-define (cons d valid) dir)] - #:do [(define neighbor (add-posns d S))] - #:when (member (hash-ref grid neighbor 'none) valid)) - neighbor)) - -(define (to-next-pipe current previous grid [acc '()]) - (cond - [(equal? (hash-ref grid current #f) #\S) acc] - [else - (define next - (for/first ([d (in-list (pipe-neighbors (hash-ref grid current)))] - #:do [(define neighbor (add-posns d current))] - #:unless (equal? neighbor previous)) - neighbor)) - (~> next (to-next-pipe _ current grid (cons current acc)))])) - -;; part 1 -(define input (fetch-aoc-input (find-session) 2023 10 #:cache #true)) - -(define pipe-grid (make-pipe-grid input)) - -(define S-posn - (for/first ([(k v) (in-hash pipe-grid)] #:when (equal? v #\S)) - k)) - -(define S-neighbors (get-valid-S-neighbors S-posn pipe-grid)) - -(define pipe-loop (to-next-pipe (first S-neighbors) S-posn pipe-grid '())) - -(/ (add1 (length pipe-loop)) 2) - -;; part 2 -(define pipe-loop-set (~> (list->set pipe-loop) (set-add S-posn))) - -(define (trace-rays pt pipes grid) - (cond - [(set-member? pipes pt) #f] - [else (odd? (trace-ray pt pipes grid))])) - -(define (trace-ray pt pipes grid) - (define row (posn-r pt)) - (for/fold ([acc 0] - [corner #f] - #:result acc) - ([col (in-naturals (posn-c pt))] - #:do [(define test-pt (posn row col))] - #:break (not (hash-has-key? grid test-pt)) - #:when (set-member? pipes test-pt)) - (define pipe (hash-ref grid test-pt)) - (match* (corner pipe) - [(#f #\|) (values (add1 acc) #f)] ; vertical crossing - [(#f (or #\F #\L)) (values acc pipe)] - [(#\F #\J) (values (add1 acc) #f)] ; a ┏━┛ shape counts as a vertical crossing - [(#\L #\7) (values (add1 acc) #f)] - [(#\F #\7) (values acc #f)] ; a ┏━┓ shape doesn't count - [(#\L #\J) (values acc #f)] - [(_ _) (values acc corner)]))) - -(~> pipe-grid - hash-keys - (count (λ~> (trace-rays pipe-loop-set pipe-grid)) _)) diff --git a/aoc2023-racket/day-11/day-11.rkt b/aoc2023-racket/day-11/day-11.rkt deleted file mode 100644 index dba617b..0000000 --- a/aoc2023-racket/day-11/day-11.rkt +++ /dev/null @@ -1,40 +0,0 @@ -#lang racket - -(require advent-of-code - threading) - -(struct posn (x y) #:transparent) - -(define input - (~> (fetch-aoc-input (find-session) 2023 11 #:cache #true) - (string-split "\n") - (map string->list _))) - -(define (get-empty-ranks grid) - (for/list ([(rank n) (in-indexed grid)] #:when (equal? '(#\.) (remove-duplicates rank))) - n)) - -(define (count-prior-empty-ranks rank empty-ranks) - (~> empty-ranks - (takef (curryr < rank)) - length)) - -(define empty-rows (get-empty-ranks input)) -(define empty-columns (get-empty-ranks (apply map list input))) ;; transpose - -(define (sum-of-star-distances in expand-by) - (define stars - (for*/list ([(row x) (in-indexed in)] - [(col y) (in-indexed row)] - #:when (equal? col #\#)) - (posn (+ x (* (sub1 expand-by) (count-prior-empty-ranks x empty-rows))) - (+ y (* (sub1 expand-by) (count-prior-empty-ranks y empty-columns)))))) - (for/sum ([star-pair (in-combinations stars 2)]) - (match-define (list (posn x1 y1) (posn x2 y2)) star-pair) - (+ (abs (- x1 x2)) (abs (- y1 y2))))) - -;; part 1 -(sum-of-star-distances input 2) - -;; part 2 -(sum-of-star-distances input 1000000) diff --git a/aoc2023-racket/day-12/day-12.rkt b/aoc2023-racket/day-12/day-12.rkt deleted file mode 100644 index 50b14bb..0000000 --- a/aoc2023-racket/day-12/day-12.rkt +++ /dev/null @@ -1,65 +0,0 @@ -#lang racket - -(require advent-of-code - threading - memo) - -(struct condition (template spring-set)) - -(define conditions - (for/list ([line (in-lines (open-aoc-input (find-session) 2023 12 #:cache #true))]) - (match (string-split line #px"[ ,]") - [(list* template spring-set) - (condition (string->list template) (map string->number spring-set))]))) - -(define/memoize (do-count template spring-group left-in-group need-gap?) - ;; template: list of spring positions - ;; spring-group: list of remaining contiguous groups of damaged springs - ;; left-in-group: springs remaining in current bad spring group being placed - ;; need-gap?: did we just finish placing a bad spring group - ;; and need at least one undamaged spring before starting the next one? - (match* (template spring-group left-in-group need-gap?) - ;; no springs left to place and no places left to place springs - ;; this is an OK arrangement, count it - [('() '() 0 _) 1] - ;; ambiguous wildcard, try both skipping this spot and starting a damaged spring group here - [((list* #\? t-rest) (list* g g-rest) 0 #f) - (+ (do-count t-rest g-rest (sub1 g) (= g 1)) - (do-count t-rest spring-group 0 #f))] - ;; definitely a place for a good spring (.), move on without consuming any spring groups - [((list* #\? t-rest) '() 0 #f) ; good spring, no more damaged springs to place - (do-count t-rest spring-group 0 #f)] - [((list* #\? t-rest) _ 0 #t) ; good spring right after finishing a group of bad springs - (do-count t-rest spring-group 0 #f)] - [((list* #\. t-rest) _ 0 _) ; known good spring - (do-count t-rest spring-group 0 #f)] - ;; start of bad spring (#) group, use the next spring group and remove 1 from it - [((list* #\# t-rest) (list* g g-rest) 0 #f) (do-count t-rest g-rest (sub1 g) (= g 1))] - ;; continuation of bad spring group, same - [((list* (or #\? #\#) t-rest) g left #f) (do-count t-rest g (sub1 left) (= left 1))] - ;; if nothing above works, this arrangement's invalid - [(_ _ _ _) 0])) - -(define (count-solutions c) - (match-define (condition template spring-set) c) - (do-count template spring-set 0 #f)) - -;; part 1 -(for/sum ([c (in-list conditions)]) - (count-solutions c)) - -;; part 2 -(define expanded-conditions - (for/list ([c (in-list conditions)]) - (condition (~> c - condition-template - (make-list 5 _) - (add-between #\?) - flatten) - (~> c - condition-spring-set - (make-list 5 _) - flatten)))) - -(for/sum ([c* (in-list expanded-conditions)]) - (count-solutions c*)) diff --git a/aoc2023-racket/day-13/day-13.rkt b/aoc2023-racket/day-13/day-13.rkt deleted file mode 100644 index 47718f8..0000000 --- a/aoc2023-racket/day-13/day-13.rkt +++ /dev/null @@ -1,47 +0,0 @@ -#lang racket - -(require advent-of-code - threading) - -(define input - (~>(fetch-aoc-input (find-session) 2023 13 #:cache #true) - (string-split "\n\n") - (map (λ~> string-split) _))) - -(define (do-symmetric? lefts rights errs) - (cond - [(empty? rights) #f] - [else - (define found-errs - (for/sum ([l (in-string (string-join lefts ""))] - [r (in-string (string-join rights ""))] - #:unless (char=? l r)) - 1)) - (if (= errs found-errs) - (length lefts) - (do-symmetric? (cons (first rights) lefts) - (rest rights) - errs))])) - -(define (symmetric? xss errs) - (do-symmetric? (list (first xss)) (rest xss) errs)) - -(define (transpose strs) - (~> strs - (map string->list _) - (apply map list _) - (map list->string _))) - -(define (find-symmetry-score xss errs) - (cond - [(symmetric? xss errs) => (curry * 100)] - [else (symmetric? (transpose xss) errs)])) - -;; part 1 -(for/sum ([note (in-list input)]) - (find-symmetry-score note 0)) - -;; part 2 -(for/sum ([note (in-list input)]) - (find-symmetry-score note 1)) - diff --git a/aoc2023-racket/day-14/day-14.rkt b/aoc2023-racket/day-14/day-14.rkt deleted file mode 100644 index d0b7cad..0000000 --- a/aoc2023-racket/day-14/day-14.rkt +++ /dev/null @@ -1,49 +0,0 @@ -#lang racket - -(require advent-of-code - threading - "../../jj-aoc.rkt") - -(define input - (~> (fetch-aoc-input (find-session) 2023 14 #:cache #true) - string-split - (map string->list _) - transpose)) - -(define (roll-boulders board) - (for/list ([col (in-list board)]) - (~> col (chunks-by (curry equal? #\#)) (append-map (curryr sort char>?) _)))) - -(define (score board) - (for*/sum ([col (in-list board)] - [(row n) (in-indexed (reverse col))] - #:when (equal? row #\O)) - (add1 n))) - -;; part 1 -(~> input - roll-boulders - score) - -;; part 2 -(define (rotate-board xss) - (~> xss - (map reverse _) - transpose)) - -(define (full-cycle board) - (foldl (λ (_ acc) (~> acc roll-boulders rotate-board)) board (range 4))) - -(define (spin-to-win board) - (define cache (make-hash)) - (define (do-spin board n) - (match (hash-ref cache board 'not-found) - ['not-found - (hash-set! cache board n) - (do-spin (full-cycle board) (sub1 n))] - [seen - (define to-end (modulo n (- seen n))) - (score (foldl (λ (_ acc) (full-cycle acc)) board (range to-end)))])) - (do-spin board 1000000000)) - -(~> input spin-to-win) diff --git a/aoc2023-racket/day-15/day-15.rkt b/aoc2023-racket/day-15/day-15.rkt deleted file mode 100644 index d049565..0000000 --- a/aoc2023-racket/day-15/day-15.rkt +++ /dev/null @@ -1,41 +0,0 @@ -#lang racket - -(require advent-of-code - threading) - -(define input - (~> (fetch-aoc-input (find-session) 2023 15 #:cache #true) string-trim (string-split ","))) - -(define (hash-algorithm str) - (for/fold ([acc 0]) ([c (in-string str)]) - (~> c char->integer (+ acc) (* 17) (modulo _ 256)))) - -;; part 1 -(for/sum ([code (in-list input)]) (hash-algorithm code)) - -;; part 2 -(define (remove-lens boxes label) - (hash-update boxes - (hash-algorithm label) - (λ (lens-set) (remove label lens-set (λ (rem l) (equal? rem (car l))))) - '())) - -(define (insert-lens boxes label focal) - (define new-lens (cons label focal)) - (hash-update boxes - (hash-algorithm label) - (λ (lens-set) - (if (assoc label lens-set) - (map (λ (pair) (if (equal? (car pair) label) new-lens pair)) lens-set) - (append lens-set (list new-lens)))) - (list new-lens))) - -(define (focusing-power boxes) - (for*/sum ([(box-number lenses) (in-hash boxes)] [(lens order) (in-indexed lenses)]) - (* (add1 box-number) (add1 order) (cdr lens)))) - -(for/fold ([boxes (hash)] #:result (focusing-power boxes)) ([code (in-list input)]) - (match code - [(regexp #rx"(.*)=(.*)" (list _ label (app string->number focal))) - (insert-lens boxes label focal)] - [(regexp #rx"(.*)-" (list _ label)) (remove-lens boxes label)])) diff --git a/aoc2023-racket/day-16/day-16.rkt b/aoc2023-racket/day-16/day-16.rkt deleted file mode 100644 index 4a70de8..0000000 --- a/aoc2023-racket/day-16/day-16.rkt +++ /dev/null @@ -1,70 +0,0 @@ -#lang racket - -(require advent-of-code - threading) - -(struct posn (r c) #:transparent) -(struct light (posn dir) #:transparent) - -(define input (fetch-aoc-input (find-session) 2023 16 #:cache #true)) - -(define grid - (for*/hash ([(row r) (in-indexed (string-split input "\n"))] - [(cell c) (in-indexed (in-string row))]) - (values (posn r c) cell))) - -(define/match (move _l) - [((light (posn r c) 'up)) (light (posn (sub1 r) c) 'up)] - [((light (posn r c) 'right)) (light (posn r (add1 c)) 'right)] - [((light (posn r c) 'left)) (light (posn r (sub1 c)) 'left)] - [((light (posn r c) 'down)) (light (posn (add1 r) c) 'down)]) - -(define/match (transform l _cell) - [(_ #\.) l] - [(_ 'off) '()] - - [((light _ (or 'up 'down)) #\|) l] - [((light _ (or 'left 'right)) #\-) l] - - [((light p 'left) #\/) (light p 'down)] - [((light p 'down) #\/) (light p 'left)] - [((light p 'right) #\/) (light p 'up)] - [((light p 'up) #\/) (light p 'right)] - - [((light p 'left) #\\) (light p 'up)] - [((light p 'up) #\\) (light p 'left)] - [((light p 'right) #\\) (light p 'down)] - [((light p 'down) #\\) (light p 'right)] - - [((light p (or 'left 'right)) #\|) (list (light p 'up) (light p 'down))] - [((light p (or 'up 'down)) #\-) (list (light p 'left) (light p 'right))]) - -;; part 1 -(define (get-energized start) - (for/fold ([energized (set)] - [previously-visited (set)] - [beam-tips (set start)] - #:result (set-count energized)) - ([_ (in-naturals)]) - (define next-positions - (list->set - (flatten (for/list ([b (in-set beam-tips)]) - (~> b move ((λ (b) (transform b (hash-ref grid (light-posn b) 'off))))))))) - (define all-visited (set-union previously-visited next-positions)) - (define next-energized (set-union energized (list->set (set-map next-positions light-posn)))) - #:break (equal? previously-visited all-visited) - (values next-energized all-visited (set-subtract next-positions previously-visited)))) - -(get-energized (light (posn 0 -1) 'right)) - -;; part 2 -(define rows (~> input (string-split "\n") length)) -(define cols (~> input (string-split #rx"\n.*") first string-length)) - -(define all-starting-positions - (append (map (λ (r) (light (posn r -1) 'right)) (range rows)) - (map (λ (r) (light (posn r cols) 'left)) (range rows)) - (map (λ (c) (light (posn -1 c) 'down)) (range cols)) - (map (λ (c) (light (posn rows c) 'up)) (range cols)))) - -(get-energized (argmax get-energized all-starting-positions)) \ No newline at end of file diff --git a/aoc2023-racket/day-17/day-17.rkt b/aoc2023-racket/day-17/day-17.rkt deleted file mode 100644 index 05709ad..0000000 --- a/aoc2023-racket/day-17/day-17.rkt +++ /dev/null @@ -1,86 +0,0 @@ -#lang racket - -(require advent-of-code - threading - data/heap) - -(struct state (p heat-lost previous history)) -(struct posn (r c)) - -(define/match (add _p1 _p2) - [((posn r1 c1) (posn r2 c2)) (posn (+ r1 r2) (+ c1 c2))]) - -(define deltas (list (posn 0 1) (posn 0 -1) (posn 1 0) (posn -1 0))) - -(define input (fetch-aoc-input (find-session) 2023 17 #:cache #true)) - -(define grid - (for*/hash ([(row r) (in-indexed (in-list (string-split input "\n")))] - [(col c) (in-indexed (in-string row))]) - (values (posn r c) (~> col string string->number)))) - -(define goal-posn (~>> grid hash-keys (argmax (λ (p) (+ (posn-r p) (posn-c p)))))) - -(define (make-key s) - (cons (state-p s) (same-dir s))) - -(define (goal? n s) - (and (equal? goal-posn (state-p s)) - (>= (length (same-dir s)) n))) - -(define (same-dir s) - (define history (state-history s)) - (if (empty? history) - '() - (takef history (λ (n) (equal? n (car history)))))) - -(define (find-good-neighbors min-dist max-dist s) - (match-define (state p hl prev hist) s) - - (define (eliminate-bad-neighbors delta) - (define neighbor (add p delta)) - (cond - [(or (equal? neighbor prev) (not (hash-has-key? grid neighbor))) #false] - [else - (define same (same-dir s)) - (define l (length same)) - (cond - [(= max-dist l) (not (equal? delta (car same)))] - [(= l 0) #true] - [(< l min-dist) (equal? delta (car same))] - [else #t])])) - - (define (make-state delta) - (define neighbor (add p delta)) - (define new-loss (+ hl (hash-ref grid neighbor))) - (state neighbor new-loss p (cons delta hist))) - - (~>> deltas (filter eliminate-bad-neighbors) (map make-state))) - -(define (find-path neighbor-fn goal-fn) - (define seen (mutable-set)) - (define queue (make-heap (λ (a b) (<= (state-heat-lost a) (state-heat-lost b))))) - (heap-add! queue (state (posn 0 0) 0 'none '())) - - (let bfs () - (define s (heap-min queue)) - (heap-remove-min! queue) - (define key (make-key s)) - (cond - [(set-member? seen key) (bfs)] - [else - (set-add! seen key) - (define neighbors (neighbor-fn s)) - (define final (findf goal-fn neighbors)) - (if final - (state-heat-lost final) - (begin - (for ([n (in-list neighbors)]) - (heap-add! queue n)) - (bfs)))]))) - -;; part 1 -(find-path (curry find-good-neighbors 0 3) (curry goal? 1)) - -;; part 2 -(find-path (curry find-good-neighbors 4 10) (curry goal? 4)) \ No newline at end of file diff --git a/aoc2023-racket/day-18/day-18.rkt b/aoc2023-racket/day-18/day-18.rkt deleted file mode 100644 index b589e41..0000000 --- a/aoc2023-racket/day-18/day-18.rkt +++ /dev/null @@ -1,48 +0,0 @@ -#lang racket -(require advent-of-code - threading) - -(struct coord (x y)) - -(define input (~> (fetch-aoc-input (find-session) 2023 18 #:cache #true))) - -(define (go-to-next-coord c dir dist) - (match-define (coord x y) c) - (match dir - [(or "R" "0") (coord (+ x dist) y)] - [(or "D" "1") (coord x (- y dist))] - [(or "L" "2") (coord (- x dist) y)] - [(or "U" "3") (coord x (+ y dist))])) - -(define/match (triangle-area _coord1 _coord2) - [((coord x1 y1) (coord x2 y2)) (/ (- (* x1 y2) (* x2 y1)) 2)]) - -(define (find-area-using parser) - (for/fold ([area 0] - [perimeter 0] - [current-coord (coord 0 0)] - #:result (+ 1 (abs area) (/ perimeter 2))) - ([dig (in-list (string-split input "\n"))]) - (define-values (dir dist) (parser dig)) - (define next-coord (go-to-next-coord current-coord dir dist)) - (values (+ area (triangle-area current-coord next-coord)) - (+ perimeter dist) next-coord))) - -;; part 1 -(define (parse-front dig) - (match-define (regexp #rx"(.) (.*) \\((.*)\\)" - (list _ dir (app string->number dist) _hex)) - dig) - (values dir dist)) - -(find-area-using parse-front) - -;; part 2 - -(define (parse-hex dig) - (match-define (regexp #rx".*\\(#(.....)(.)\\)" - (list _ (app (curryr string->number 16) dist) dir)) - dig) - (values dir dist)) - -(find-area-using parse-hex) diff --git a/aoc2023-racket/day-19/day-19.rkt b/aoc2023-racket/day-19/day-19.rkt deleted file mode 100644 index f7561f6..0000000 --- a/aoc2023-racket/day-19/day-19.rkt +++ /dev/null @@ -1,134 +0,0 @@ -#lang racket - -(require advent-of-code - threading - data/applicative - data/monad - megaparsack - megaparsack/text - racket/struct) - -(struct part (x m a s)) -(struct rule (rating comparison threshold action)) -(struct just (action)) -(struct interval (from to)) - -(match-define (list raw-workflows raw-parts) - (~> (fetch-aoc-input (find-session) 2023 19 #:cache #true) - (string-split "\n\n") - (map (curryr string-split "\n") _))) - -(define/match (to-getter _) - [(#\x) part-x] - [(#\m) part-m] - [(#\a) part-a] - [(#\s) part-s]) - -(define/match (to-comp _) - [(#\>) >] - [(#\<) <]) - -(define/match (to-action _) - [('(#\R)) 'reject] - [('(#\A)) 'accept] - [(name) (apply string name)]) - -(define rule/p - (do (or/p - (try/p (do [rating <- (char-in/p "xmas")] - [comparison <- (char-in/p "<>")] - [threshold <- integer/p] - (char/p #\:) - [action <- (many+/p letter/p)] - (pure (rule (to-getter rating) - (to-comp comparison) - threshold - (to-action action))))) - (do [name <- (many+/p letter/p)] (pure (just (to-action name))))))) -(define rules/p - (do [name <- (many+/p letter/p)] - (char/p #\{) - [rules <- (many+/p rule/p #:sep (char/p #\,))] - (char/p #\}) - (pure (cons (list->string name) rules)))) - -(define rating/p (do letter/p (char/p #\=) integer/p)) -(define parts/p - (do (string/p "{") - [ratings <- (many/p rating/p #:sep (char/p #\,) #:min 4 #:max 4)] - (string/p "}") - (pure (apply part ratings)))) - -(define workflows - (~>> raw-workflows - (map (λ~>> (parse-string rules/p) parse-result!)) - make-immutable-hash)) -(define parts (map (λ~>> (parse-string parts/p) parse-result!) raw-parts)) - -;; part 1 - -(define (evaluate-workflow p [workflow-name "in"]) - (define rules (hash-ref workflows workflow-name)) - (match (evaluate-rules p rules) - ['accept (~> p struct->list (apply + _))] - ['reject 0] - [name (evaluate-workflow p name)])) - -(define (evaluate-rules p rules) - (match rules - [(list* (just action) _) action] - [(list* (rule rating comparison threshold action) _) - #:when (comparison (rating p) threshold) - action] - [(list* _ tail) (evaluate-rules p tail)])) - -(for/sum ([p (in-list parts)]) (evaluate-workflow p)) - -;; part 2 - -(define (part-update-range pr rating i) - (match rating - [(== part-x) (struct-copy part pr (x i))] - [(== part-m) (struct-copy part pr (m i))] - [(== part-a) (struct-copy part pr (a i))] - [(== part-s) (struct-copy part pr (s i))])) - -(define (evaluate-workflow-on-range pr [workflow-name "in"]) - (define rules (hash-ref workflows workflow-name)) - (evaluate-rules-on-range pr rules)) - -(define (evaluate-rules-on-range pr rules) - (match rules - [(list* (just 'accept) _) - (~> pr struct->list - (map (λ (i) (add1 (- (interval-to i) (interval-from i)))) _) - (apply * _))] - [(list* (just 'reject) _) 0] - [(list* (just name) _) (evaluate-workflow-on-range pr name)] - [(list* (rule rating (== <) threshold action) tail) - (match-define (interval i-min i-max) (rating pr)) - (split-range pr - rating - (interval i-min (sub1 threshold)) - action - (interval threshold i-max) - tail)] - [(list* (rule rating (== >) threshold action) tail) - (match-define (interval i-min i-max) (rating pr)) - (split-range pr - rating - (interval (add1 threshold) i-max) - action - (interval i-min threshold) - tail)])) - -(define (split-range pr rating keep action pass rules) - (+ (evaluate-rules-on-range (part-update-range pr rating keep) - (list (just action))) - (evaluate-rules-on-range (part-update-range pr rating pass) - rules))) - -(define start-interval (interval 1 4000)) - -(evaluate-workflow-on-range - (part start-interval start-interval start-interval start-interval)) diff --git a/aoc2023-racket/day-20/day-20.rkt b/aoc2023-racket/day-20/day-20.rkt deleted file mode 100644 index 2e3852d..0000000 --- a/aoc2023-racket/day-20/day-20.rkt +++ /dev/null @@ -1,144 +0,0 @@ -#lang racket - -(require advent-of-code - threading - data/applicative - data/monad - megaparsack - megaparsack/text) - -(struct broadcaster ()) -(struct flipflop (state received)) -(struct conjunction (recieved)) -(struct cable (type dests)) -(struct nothing ()) - -(define charlist->symbol (λ~>> (apply string) string->symbol)) - -(define input (fetch-aoc-input (find-session) 2023 20 #:cache true)) - -(define module/p - (do (or/p (do (char/p #\%) - [name <- (many+/p letter/p)] - (pure (cons (charlist->symbol name) (flipflop 'off '())))) - (do (char/p #\&) - [name <- (many+/p letter/p)] - (pure (cons (charlist->symbol name) (conjunction (hash))))) - (do [name <- (many+/p letter/p)] - (pure (cons (charlist->symbol name) (broadcaster))))))) - -(define cable/p - (do [mod <- module/p] - (string/p " -> ") - [names <- (many/p (many+/p letter/p) #:sep (string/p ", "))] - (pure (cable mod (map charlist->symbol names))))) - -(define cables (~> input (string-split "\n") (map (λ~>> (parse-string cable/p) parse-result!) _))) - -(define destinations - (for/hash ([cable (in-list cables)]) - (values (car (cable-type cable)) (cable-dests cable)))) - -(define (set-conjunction-initial-state c) - (cond - [(conjunction? (cdr c)) - (~>> destinations - hash-keys - (filter (λ (k) (member (car c) (hash-ref destinations k)))) - (map (λ (k) (cons k 'low))) - (make-immutable-hash) - conjunction - (cons (car c)))] - [else c])) - -(define (make-initial-conditions-hash cables) - (~>> cables - (map cable-type) - (map set-conjunction-initial-state) - make-immutable-hash)) - -(define (receive mod from tone) - (match mod - [(flipflop state queue) (flipflop state (append queue (list tone)))] - [(conjunction received) (conjunction (hash-set received from tone))] - [(nothing) (nothing)])) - -; needed for part 2 -(define to-rx '(rk cd zf qx)) -(define sentry-tones (make-hash (for/list ([node to-rx]) (cons node 0)))) - -(define (press-button-once current-state this-round) - (for/fold ([queue '(broadcaster)] - [all-cables-state current-state] - [high 0] - [low 0] - #:result (values all-cables-state high low)) - ([_i (in-naturals)] #:break (empty? queue)) - (match-define (list* hd tl) queue) - (define to (hash-ref destinations hd (nothing))) - (match (hash-ref all-cables-state hd) - [(broadcaster) - (define state* - (foldl (λ (r acc) (hash-update acc r (λ~> (receive hd 'low)) (nothing))) - all-cables-state - to)) - (values (hash-ref destinations 'broadcaster) state* high (+ (length to) (add1 low)))] - [(flipflop 'off (list* 'low q)) - (define state* - (~> all-cables-state - (foldl (λ (r acc) - (when (member r to-rx) - (println (~a r " received high tone at " this-round))) - (hash-update acc r (λ~> (receive hd 'high)) (nothing))) - _ - to) - (hash-set _ hd (flipflop 'on q)))) - (values (append tl to) state* (+ (length to) high) low)] - [(flipflop 'on (list* 'low q)) - (define state* - (~> all-cables-state - (foldl (λ (r acc) (hash-update acc r (λ~> (receive hd 'low)) (nothing))) _ to) - (hash-set _ hd (flipflop 'off q)))) - (values (append tl to) state* high (+ (length to) low))] - [(flipflop on-or-off (list* 'high q)) - (define state* (~> all-cables-state (hash-set _ hd (flipflop on-or-off q)))) - (values tl state* high low)] - [(conjunction received) - #:when (or (empty? (hash-values received)) (member 'low (hash-values received))) - - (when (member hd to-rx) - (hash-set! sentry-tones hd this-round)) - (define state* - (foldl (λ (r acc) - (hash-update acc r (λ~> (receive hd 'high)) (nothing))) - all-cables-state - to)) - (values (append to tl) state* (+ (length to) high) low)] - [(conjunction _) - (define state* - (foldl (λ (r acc) (hash-update acc r (λ~> (receive hd 'low)) (nothing))) - all-cables-state - to)) - (values (append tl to) state* high (+ (length to) low))] - [(nothing) (values tl all-cables-state high low)]))) - -;; part 1 -(for/fold ([starting-state (make-initial-conditions-hash cables)] - [high 0] - [low 0] - #:result (* high low)) - ([i (in-range 1000)]) - (define-values (next-state this-high this-low) (press-button-once starting-state i)) - (values next-state (+ high this-high) (+ low this-low))) - -;; part 2 -;; rx receives a tone from gh, which receives four tones itself -;; those tones arrive on regular synced cycles so it's just the LCM of those cycle lengths -;; and since those cycle lengths are prime, it reduces to the product of the lengths -;; this is a really hacky mutable solution, I'm sure there's better ways of flagging these cycles - -(for/fold ([starting-state (make-initial-conditions-hash cables)] - #:result (apply * (hash-values sentry-tones))) - ([i (in-range 1 5000)]) - (define-values (next-state _high _low) (press-button-once starting-state i)) - (values next-state)) \ No newline at end of file diff --git a/aoc2023-racket/day-21/day-21.rkt b/aoc2023-racket/day-21/day-21.rkt deleted file mode 100644 index b5478eb..0000000 --- a/aoc2023-racket/day-21/day-21.rkt +++ /dev/null @@ -1,69 +0,0 @@ -#lang racket - -(require advent-of-code - threading - simple-polynomial - racket/hash) - -(define input (fetch-aoc-input (find-session) 2023 21 #:cache #true)) - -(define initial-garden - (~> (for*/list ([(row r) (in-indexed (string-split input "\n"))] - [(col c) (in-indexed row)] - #:unless (equal? col #\#)) - (cons (cons r c) (if (equal? col #\S) 'on 'off))) - make-hash)) - -(define (neighbors p) - (match-define (cons r c) p) - (list (cons (add1 r) c) (cons (sub1 r) c) (cons r (add1 c)) (cons r (sub1 c)))) - -(define (make-n-steps garden n) - (define g (hash-copy garden)) - (define (make-one-step) - (define update (make-hash)) - (for ([(cons state) (in-hash g)] #:when (equal? state 'on)) - (hash-set! update cons 'off) - (for ([neighbor (in-list (neighbors cons))] #:when (hash-has-key? g neighbor)) - (hash-set! update neighbor 'on))) - (hash-union! g update #:combine/key (λ (_k _v v) v))) - (for/fold ([_ void] - #:result (~>> g hash-values (count (curry equal? 'on)))) - ([i (in-range n)]) - (displayln i) - (make-one-step))) - -;; part 1 -(make-n-steps initial-garden 64) - -;; part 2 -;; the growth of the steps pattern is regular and quadratic -;; the rock pattern has aisles in it that allow the steps pattern to expand freely -;; such that it will cross from one side to another in X steps -;; where X is the height/width of the repeated section - -(define grid-size (~> input (string-split "\n") length)) -(define half-size (/ (sub1 grid-size) 2)) - -(define expanded-garden - (~> (for*/list (#:do [(define rows (string-split input "\n"))] - #:do [(define height (length rows))] - [(row r) (in-indexed rows)] - #:do [(define width (string-length row))] - [(col c) (in-indexed row)] - #:unless (equal? col #\#) - [n (in-inclusive-range -3 3)] - [m (in-inclusive-range -3 3)]) - - (cons (cons (+ r (* n height)) (+ c (* m width))) - (if (and (= n m 0) (equal? col #\S)) 'on 'off))) - make-hash)) - -(define fitting-points - (for/list ([n (in-range 3)] #:do [(define size (+ half-size (* n grid-size)))]) - (cons n (make-n-steps expanded-garden size)))) - -(define end-cycle 26501365) -(define x (/ (- end-cycle half-size) grid-size)) - -((points->polynomial fitting-points) x) \ No newline at end of file diff --git a/aoc2023-racket/day-22/day-22.rkt b/aoc2023-racket/day-22/day-22.rkt deleted file mode 100644 index 53668c0..0000000 --- a/aoc2023-racket/day-22/day-22.rkt +++ /dev/null @@ -1,109 +0,0 @@ -#lang racket - -(require advent-of-code - threading - data/applicative - data/monad - megaparsack - megaparsack/text - racket/hash) - -(struct posn (x y z)) -(struct block (n from to)) - -(define input (fetch-aoc-input (find-session) 2023 22 #:cache #true)) - -(define coordinate/p - (do [coords <- (many/p integer/p #:sep (char/p #\,) #:min 3 #:max 3)] - (pure (apply posn coords)))) - -(define block/p - (do [from <- coordinate/p] - (char/p #\~) - [to <- coordinate/p] - (pure (cons from to)))) - -(define starting-blocks - (~> (for/list ([line (in-list (string-split input "\n"))] - [n (in-naturals)]) - (match-define (cons from to) (parse-result! (parse-string block/p line))) - (block n from to)) - (sort < #:key (λ~> block-from posn-z)))) - -(define (all-in-cross-section-at-level b z) - (match-define (block _ (posn x1 y1 _) (posn x2 y2 _)) b) - (for*/list ([x (in-inclusive-range x1 x2)] - [y (in-inclusive-range y1 y2)]) - (posn x y z))) - -(define (place-block-at-level b h dz) - (match-define (block n (posn x1 y1 z1) (posn x2 y2 z2)) b) - (define now-occupied - (for*/hash ([x (in-inclusive-range x1 x2)] - [y (in-inclusive-range y1 y2)] - [z (in-inclusive-range dz (+ dz (- z2 z1)))]) - (values (posn x y z) n))) - (hash-union h now-occupied)) - -(define (find-lowest-level b h [z (~> b block-from posn-z)]) - (cond - [(= z 0) - (place-block-at-level b h 1)] - [(findf (curry hash-has-key? h) (all-in-cross-section-at-level b z)) - (place-block-at-level b h (add1 z))] - [else - (find-lowest-level b h (sub1 z))])) - -(define blocks-in-space (foldl find-lowest-level (hash) starting-blocks)) -(define block-positions - (for/fold ([placed-blocks (hash)]) - ([(p n) (in-hash blocks-in-space)]) - (hash-update placed-blocks n (curryr set-add p) (set)))) - -(define (down-one p) - (match p - [(posn x y z) (posn x y (sub1 z))])) - -(define supporting-blocks - (for/hash ([(n-id n-posns) (in-hash block-positions)]) - (values n-id - (for*/set ([(m-id m-posns) (in-hash block-positions)] - #:unless (= n-id m-id) - [m-posn (in-set m-posns)] - #:when (set-member? n-posns (down-one m-posn))) - m-id)))) - -(define supported-by-blocks - (for/hash ([n-id (in-hash-keys supporting-blocks)]) - (define supporters - (~> (for*/set - ([(m-id m-supporting) (in-hash supporting-blocks)] - #:unless (= n-id m-id) - #:when (set-member? m-supporting n-id)) - m-id) - ((λ (s) (if (set-empty? s) (set 'ground) s))))) - (values n-id supporters))) - -;; part 1 -(define vulnerable-blocks - (for/list ([n-id (in-range (length starting-blocks))] - #:when (for/or ([m-supported-by (in-hash-values supported-by-blocks)]) - (set-empty? (set-remove m-supported-by n-id)))) - n-id)) -(- (length starting-blocks) (length vulnerable-blocks)) - -;; part 2 -(for/sum ([n (in-list vulnerable-blocks)]) - (for/fold ([fallen (set n)] - [bricks (set n)] - #:result (~> fallen set-count sub1)) - ([_ (in-naturals)]) - #:break (set-empty? bricks) - (define bricks-above - (for*/set - ([brick (in-set bricks)] - [supporting (in-set (hash-ref supporting-blocks brick))] - #:when (for/and ([supports (in-set (hash-ref supported-by-blocks supporting))]) - (set-member? fallen supports))) - supporting)) - (values (set-union fallen bricks-above) bricks-above))) \ No newline at end of file diff --git a/aoc2023-racket/day-23/day-23.rkt b/aoc2023-racket/day-23/day-23.rkt deleted file mode 100644 index c048013..0000000 --- a/aoc2023-racket/day-23/day-23.rkt +++ /dev/null @@ -1,89 +0,0 @@ -#lang racket - -(require advent-of-code - threading - graph) - -(define input (fetch-aoc-input (find-session) 2023 23 #:cache #true)) -(define trails - (for*/hash ([(row r) (in-indexed (string-split input "\n"))] - [(col c) (in-indexed row)] - #:when (not (equal? col #\#))) - ; for now, we don't actually need to detect paths and slopes, just not-rocks - ; in part 1, all forks in the road go right or down, so we can just infer the path - ; direction from the shape of the junction and form a network of junctions from there - ; in part 2, the slopes are removed anyway - (values (cons (add1 r) (add1 c)) col))) - -(define max-row (~> input (string-split "\n") length)) - -(define start (findf (λ (p) (= (car p) 1)) (hash-keys trails))) -(define end (findf (λ (p) (= (car p) max-row)) (hash-keys trails))) - -(define (get-neighbors posn type) - (match-define (cons r c) posn) - (match type - ['junction - (~> (set (cons (add1 r) c) (cons r (add1 c))))] - [_ - (~> (list (cons (add1 r) c) (cons (sub1 r) c) (cons r (add1 c)) (cons r (sub1 c))) - (filter (curry hash-has-key? trails) _) - list->set)])) - -(define junction-points - (for/set ([(k v) (in-hash trails)] - #:when (not (= (set-count (get-neighbors k v)) 2))) - k)) - -(define trails-with-junctions - (for/hash ([k (in-hash-keys trails)]) - (cond - [(set-member? junction-points k) (values k 'junction)] - [else (values k 'trail)]))) - -(define (walk-to-next-junction start current [length 1] [seen (set start)]) - (define next (~> current - (get-neighbors _ 'trail) - (set-subtract seen) set-first)) - (cond - [(equal? (hash-ref trails-with-junctions next) 'junction) - (list (- (add1 length)) start next)] ; weird format is due to graph library - [else - (walk-to-next-junction start next (add1 length) (set-add seen current))])) - -(define routes-to-junctions - (for*/list ([j (in-set junction-points)] - [neighbor (in-set (get-neighbors j 'junction))] - #:when (hash-has-key? trails neighbor)) - (walk-to-next-junction j neighbor))) - -;; part 1 -- using graph library for Bellman-Ford on negative weighted graph -;; Bellman-Ford finds the shortest path, but negating all the path weights -;; will give us the longest path instead -;; -;; unlike Dijkstra which can't handle negative path lengths, Bellman-Ford -;; works as long as the graph is currently directed and acyclic -(define slippery-trail (weighted-graph/directed routes-to-junctions)) -(match-define-values (distances _) (bellman-ford slippery-trail start)) -(- (hash-ref distances end)) - -;; part 2 -- rolling my own DFS that can reject seen junctions and dead ends -(define routes-to-junctions-with-traction - (for/fold ([trails (hash)]) ([route (in-list routes-to-junctions)]) - (match-define (list (app - weight) from to) route) - (~> trails - (hash-update _ from (curry cons (cons to weight)) '()) - (hash-update _ to (curry cons (cons from weight)) '())))) - -(define (dfs g from to [acc 0] [seen (set from)]) - (cond - [(equal? from to) acc] - [else - (define choices (filter (λ (path) (not (set-member? seen (car path)))) (hash-ref g from))) - (if (empty? choices) 0 ; long dead-ends don't count - (for/fold ([best acc]) - ([path (in-list choices)] - #:do [(match-define (cons next dist) path)]) - (max best (dfs g next to (+ acc dist) (set-add seen next)))))])) - -(dfs routes-to-junctions-with-traction start end) \ No newline at end of file diff --git a/aoc2023-racket/day-24/day-24a.rkt b/aoc2023-racket/day-24/day-24a.rkt deleted file mode 100644 index 31f526d..0000000 --- a/aoc2023-racket/day-24/day-24a.rkt +++ /dev/null @@ -1,51 +0,0 @@ -#lang rosette - -(require advent-of-code - threading) - -(struct hail (posn vel) #:transparent) -(struct posn (x y z) #:transparent) -(struct vel (x y z) #:transparent) - -(define input (fetch-aoc-input (find-session) 2023 24 #:cache #true)) - -(define LOWER-BOUND 200000000000000) -(define UPPER-BOUND 400000000000000) - -(define (->struct f str) - (~> str (string-split _ ",") (map (λ~> string-trim string->number) _) (apply f _))) - -(define (parse-hail-record str) - (match-define (list p v) (string-split str " @ ")) - (hail (->struct posn p) - (->struct vel v))) - -(define hail-paths - (for/list ([hail (in-list (string-split input "\n"))]) - (parse-hail-record hail))) - -;; part 1 -(define (valid-intersection? h1 h2) - (match-define (hail (posn x1 y1 _) (vel vx1 vy1 _)) h1) - (match-define (hail (posn x2 y2 _) (vel vx2 vy2 _)) h2) - (cond - [(= (* vy1 vx2) (* vx1 vy2)) #f] - [else - (define t1 (/ (- (* vy2 (- x1 x2)) (* vx2 (- y1 y2))) - (- (* vy1 vx2) (* vx1 vy2)))) - (define t2 (/ (- (* vy1 (- x2 x1)) (* vx1 (- y2 y1))) - (- (* vy2 vx1) (* vx2 vy1)))) - - (define x (+ x1 (* t1 vx1))) - (define y (+ y1 (* t1 vy1))) - - (and (<= LOWER-BOUND x UPPER-BOUND) - (<= LOWER-BOUND y UPPER-BOUND) - (<= 0 t1) - (<= 0 t2))])) - -(for/sum ([(trial-paths) (in-combinations hail-paths 2)] ; - #:when (apply valid-intersection? trial-paths)) - 1) - -;; part 2 - see day-24b.rkt diff --git a/aoc2023-racket/day-24/day-24b.rkt b/aoc2023-racket/day-24/day-24b.rkt deleted file mode 100644 index b106b30..0000000 --- a/aoc2023-racket/day-24/day-24b.rkt +++ /dev/null @@ -1,37 +0,0 @@ -#lang rosette - -(require advent-of-code - threading) - -(struct hail (posn vel)) -(struct posn (x y z)) -(struct vel (x y z)) - -(define input (fetch-aoc-input (find-session) 2023 24 #:cache #true)) - -(define (->struct f str) - (~> str (string-split _ ",") (map (λ~> string-trim string->number) _) (apply f _))) - -(define (parse-hail-record str) - (match-define (list p v) (string-split str " @ ")) - (hail (->struct posn p) (->struct vel v))) - -(define hail-paths - (for/list ([hail (in-list (string-split input "\n"))] ; - [_ (in-range 3)]) - (parse-hail-record hail))) - -;; part 1 - see day-24a.rkt -;; part 2 - -(define-symbolic px py pz vx vy vz integer?) - -(define sol - (solve ; - (for ([path (in-list hail-paths)]) - (define-symbolic* t integer?) - (assert (= (+ px (* vx t)) (+ (~> path hail-posn posn-x) (* (~> path hail-vel vel-x) t)))) - (assert (= (+ py (* vy t)) (+ (~> path hail-posn posn-y) (* (~> path hail-vel vel-y) t)))) - (assert (= (+ pz (* vz t)) (+ (~> path hail-posn posn-z) (* (~> path hail-vel vel-z) t))))))) - -(evaluate (+ px py pz) sol) diff --git a/aoc2023-racket/day-25/day-25.rkt b/aoc2023-racket/day-25/day-25.rkt deleted file mode 100644 index aa32e43..0000000 --- a/aoc2023-racket/day-25/day-25.rkt +++ /dev/null @@ -1,43 +0,0 @@ -#lang racket - -(require advent-of-code - threading - graph) - -(define input - (~> (fetch-aoc-input (find-session) 2023 25 #:cache #true) - (string-split "\n") - (map (curryr string-split ": ") _))) - -(define all-wires - (for*/list ([wire-diagram (in-list input)] [devices (in-list (string-split (second wire-diagram)))]) - (list (car wire-diagram) devices))) - -;; instead of trying to solve the minimum cut problem, I generated the graph and -;; rendered it in graphviz: - -; (define out (open-output-file "graphviz")) -; (~> all-wires -; unweighted-graph/undirected -; graphviz -; (display out)) -; (close-output-port out) - -;; the bottleneck is very obvious on the graph -- -;; there's two large clusters of nodes, connected by just three edges -;; -;; from the graphviz output, the three critical wires are -;; cpq-hlx -;; hqp-spk -;; chr-zlx - -(define remove-these-three '(("cpq" "hlx") ("hqp" "spk") ("chr" "zlx"))) -(define cut-wires - (for/list ([wire (in-list all-wires)] #:unless (member (sort wire string cut-wires - unweighted-graph/undirected - scc - (map length _) - (apply * _)) \ No newline at end of file diff --git a/codingquest2024/.github/workflows/test.yml b/codingquest2024/.github/workflows/test.yml deleted file mode 100644 index 916edea..0000000 --- a/codingquest2024/.github/workflows/test.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: test - -on: - push: - branches: - - master - - main - pull_request: - -jobs: - test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: erlef/setup-beam@v1 - with: - otp-version: "26.0.2" - gleam-version: "1.0.0" - rebar3-version: "3" - # elixir-version: "1.15.4" - - run: gleam deps download - - run: gleam test - - run: gleam format --check src test diff --git a/codingquest2024/.gitignore b/codingquest2024/.gitignore deleted file mode 100644 index 6fdd6db..0000000 --- a/codingquest2024/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -*.beam -*.ez -/build -erl_crash.dump -data.txt diff --git a/codingquest2024/README.md b/codingquest2024/README.md deleted file mode 100644 index 8412b48..0000000 --- a/codingquest2024/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# codingquest2024 - -[![Package Version](https://img.shields.io/hexpm/v/codingquest2024)](https://hex.pm/packages/codingquest2024) -[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/codingquest2024/) - -```sh -gleam add codingquest2024 -``` -```gleam -import codingquest2024 - -pub fn main() { - // TODO: An example of the project in use -} -``` - -Further documentation can be found at . - -## Development - -```sh -gleam run # Run the project -gleam test # Run the tests -gleam shell # Run an Erlang shell -``` diff --git a/codingquest2024/gleam.toml b/codingquest2024/gleam.toml deleted file mode 100644 index f39baa5..0000000 --- a/codingquest2024/gleam.toml +++ /dev/null @@ -1,22 +0,0 @@ -name = "codingquest2024" -version = "1.0.0" - -# Fill out these fields if you intend to generate HTML documentation or publish -# your project to the Hex package manager. -# -# description = "" -# licences = ["Apache-2.0"] -# repository = { type = "github", user = "username", repo = "project" } -# links = [{ title = "Website", href = "https://gleam.run" }] -# -# For a full reference of all the available options, you can have a look at -# https://gleam.run/writing-gleam/gleam-toml/. - -[dependencies] -gleam_stdlib = "~> 0.34 or ~> 1.0" -simplifile = "~> 1.5" -gleam_otp = "~> 0.10" -gleam_erlang = "~> 0.24" - -[dev-dependencies] -gleeunit = "~> 1.0" diff --git a/codingquest2024/manifest.toml b/codingquest2024/manifest.toml deleted file mode 100644 index 55b1f61..0000000 --- a/codingquest2024/manifest.toml +++ /dev/null @@ -1,17 +0,0 @@ -# This file was generated by Gleam -# You typically do not need to edit this file - -packages = [ - { name = "gleam_erlang", version = "0.24.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "26BDB52E61889F56A291CB34167315780EE4AA20961917314446542C90D1C1A0" }, - { name = "gleam_otp", version = "0.10.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "0B04FE915ACECE539B317F9652CAADBBC0F000184D586AAAF2D94C100945D72B" }, - { name = "gleam_stdlib", version = "0.36.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "C0D14D807FEC6F8A08A7C9EF8DFDE6AE5C10E40E21325B2B29365965D82EB3D4" }, - { name = "gleeunit", version = "1.0.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "D364C87AFEB26BDB4FB8A5ABDE67D635DC9FA52D6AB68416044C35B096C6882D" }, - { name = "simplifile", version = "1.5.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "EB9AA8E65E5C1E3E0FDCFC81BC363FD433CB122D7D062750FFDF24DE4AC40116" }, -] - -[requirements] -gleam_erlang = { version = "~> 0.24"} -gleam_otp = { version = "~> 0.10" } -gleam_stdlib = { version = "~> 0.34 or ~> 1.0" } -gleeunit = { version = "~> 1.0" } -simplifile = { version = "~> 1.5" } diff --git a/codingquest2024/src/codingquest2024.gleam b/codingquest2024/src/codingquest2024.gleam deleted file mode 100644 index 5af4a2a..0000000 --- a/codingquest2024/src/codingquest2024.gleam +++ /dev/null @@ -1,5 +0,0 @@ -import gleam/io - -pub fn main() { - io.println("Hello from codingquest2024!") -} diff --git a/codingquest2024/src/day1/solution.gleam b/codingquest2024/src/day1/solution.gleam deleted file mode 100644 index abef2eb..0000000 --- a/codingquest2024/src/day1/solution.gleam +++ /dev/null @@ -1,55 +0,0 @@ -import gleam/dict.{type Dict} -import gleam/int -import gleam/io -import gleam/list -import gleam/option.{None, Some} -import gleam/regex -import gleam/result -import gleam/string -import simplifile - -pub type Adjustment { - Adjustment(spaceliner: String, value: Int) -} - -pub fn main() { - let assert Ok(data) = simplifile.read(from: "./src/day1/data.txt") - - let raw_adjustments = string.split(data, "\n") - - raw_adjustments - |> collate_adjustments - |> dict.values - |> list.reduce(with: int.min) - |> result.unwrap(0) - |> int.to_string() - |> io.println() -} - -fn parse_line(line: String) -> Adjustment { - let assert Ok(re) = regex.from_string("[\\s:]+") - - let assert [spaceliner, kind, raw_value] = - regex.split(with: re, content: line) - - let assert Ok(value) = int.parse(raw_value) - Adjustment(spaceliner, classify_adjustment(kind) * value) -} - -fn classify_adjustment(name: String) -> Int { - case name { - "Seat" | "Meals" | "Luggage" | "Fee" | "Tax" -> 1 - "Discount" | "Rebate" -> -1 - _ -> panic as "unknown adjustment kind" - } -} - -fn collate_adjustments(raw_adjustments: List(String)) -> Dict(String, Int) { - use acc, this <- list.fold(over: raw_adjustments, from: dict.new()) - let adjustment = parse_line(this) - use maybe_v <- dict.update(in: acc, update: adjustment.spaceliner) - case maybe_v { - None -> adjustment.value - Some(v) -> v + adjustment.value - } -} diff --git a/codingquest2024/src/day2/solution.gleam b/codingquest2024/src/day2/solution.gleam deleted file mode 100644 index 59f8d29..0000000 --- a/codingquest2024/src/day2/solution.gleam +++ /dev/null @@ -1,66 +0,0 @@ -import gleam/bit_array -import gleam/list -import gleam/int -import gleam/io -import gleam/string -import simplifile - -pub type Location { - Internal - Passenger - Other -} - -pub type Packet { - Packet(length: Int, endpoint: Location) -} - -pub fn main() { - let assert Ok(data) = simplifile.read(from: "./src/day2/data.txt") - - let #(internal_packets, passenger_packets) = - data - |> string.split("\n") - |> list.map(parse_packet) - |> list.partition(fn(p) { p.endpoint == Internal }) - - { - int.to_string(sum_packet_lengths(internal_packets)) - <> "/" - <> int.to_string(sum_packet_lengths(passenger_packets)) - } - |> io.println() -} - -fn parse_packet(raw_packet: String) { - let assert Ok(<< - _:bytes-size(2), - length:int-size(16), - _:bytes-size(8), - source:bytes-size(4), - destination:bytes-size(4), - >>) = bit_array.base16_decode(raw_packet) - - Packet(length, set_endpoint(source, destination)) -} - -fn set_endpoint(source: BitArray, dest: BitArray) -> Location { - case identify_location(source), identify_location(dest) { - Other, Internal | Internal, Other -> Internal - Other, Passenger | Passenger, Other -> Passenger - _, _ -> Other - } -} - -fn identify_location(ip: BitArray) -> Location { - case ip { - <<192, 168, _, _>> -> Internal - <<10, 0, _, _>> -> Passenger - _ -> Other - } -} - -fn sum_packet_lengths(packets: List(Packet)) -> Int { - use acc, p <- list.fold(packets, 0) - acc + p.length -} diff --git a/codingquest2024/src/day3/solution.gleam b/codingquest2024/src/day3/solution.gleam deleted file mode 100644 index 6314060..0000000 --- a/codingquest2024/src/day3/solution.gleam +++ /dev/null @@ -1,50 +0,0 @@ -import gleam/bit_array -import gleam/int -import gleam/io -import gleam/string -import gleam/string_builder.{type StringBuilder} -import simplifile - -pub fn main() { - let assert Ok(data) = simplifile.read(from: "./src/day3/data.txt") - - data - |> string.split(" ") - |> build_flat_image(" ", string_builder.new()) - |> bit_array.from_string() - |> print_next_slice() -} - -fn build_flat_image( - nums: List(String), - pixel: String, - acc: StringBuilder, -) -> String { - case nums { - [] -> string_builder.to_string(acc) - [h, ..t] -> { - let assert Ok(n) = int.parse(h) - let pixels = string.repeat(pixel, n) - build_flat_image(t, next_pixel(pixel), string_builder.append(acc, pixels)) - } - } -} - -fn print_next_slice(str: BitArray) -> Nil { - case str { - <> -> { - let assert Ok(out) = bit_array.to_string(slice) - io.println(out) - print_next_slice(rest) - } - _ -> Nil - } -} - -fn next_pixel(p: String) -> String { - case p { - " " -> "#" - "#" -> " " - _ -> panic - } -} diff --git a/codingquest2024/src/day4/solution.gleam b/codingquest2024/src/day4/solution.gleam deleted file mode 100644 index a03b2be..0000000 --- a/codingquest2024/src/day4/solution.gleam +++ /dev/null @@ -1,49 +0,0 @@ -import gleam/float -import gleam/io -import gleam/list -import gleam/regex -import gleam/result -import gleam/string -import simplifile - -pub type Star { - Star(name: String, x: Float, y: Float, z: Float) -} - -pub fn main() { - let assert Ok(data) = simplifile.read(from: "./src/day4/data.txt") - - let assert [_, ..stars] = string.split(data, "\n") - - stars - |> list.map(parse_star_record) - |> list.combination_pairs() - |> list.map(star_distance) - |> list.reduce(float.min) - |> io.debug -} - -fn parse_star_record(str: String) -> Star { - let assert Ok(re) = regex.from_string("\\s{2,}") - - let assert [name, ..coords] = regex.split(with: re, content: str) - let assert [_, x, y, z] = - coords - |> list.map(float.parse) - |> result.values - - Star(name, x, y, z) -} - -fn star_distance(stars: #(Star, Star)) -> Float { - let #(star1, star2) = stars - let assert Ok(dist) = - float.square_root( - sq(star1.x -. star2.x) +. sq(star1.y -. star2.y) +. sq(star1.z -. star2.z), - ) - dist -} - -fn sq(x: Float) -> Float { - x *. x -} diff --git a/codingquest2024/src/day5/solution.gleam b/codingquest2024/src/day5/solution.gleam deleted file mode 100644 index 6c10693..0000000 --- a/codingquest2024/src/day5/solution.gleam +++ /dev/null @@ -1,67 +0,0 @@ -import gleam/dict.{type Dict} -import gleam/io -import gleam/int -import gleam/list -import gleam/regex -import gleam/result -import gleam/string -import simplifile - -type Atlas = - Dict(String, Dict(String, Int)) - -pub fn main() { - let assert Ok(data) = simplifile.read(from: "./src/day5/data.txt") - - let assert [table, routes] = string.split(data, "\n\n") - let assert [header, ..rows] = string.split(table, "\n") - - let dests = - header - |> string.trim() - |> split_on_many_spaces - - let atlas = build_atlas(rows, dests) - - routes - |> string.split("\n") - |> list.fold(0, fn(acc, route) { acc + find_total_distance(route, atlas) }) - |> io.debug -} - -fn split_on_many_spaces(str) { - let assert Ok(re_spaces) = regex.from_string("\\s+") - regex.split(re_spaces, str) -} - -fn build_atlas(rows, dests) { - rows - |> list.map(split_on_many_spaces) - |> list.fold(dict.new(), fn(acc, row) { - let assert [from, ..raw_dists] = row - let assert Ok(dists) = list.try_map(raw_dists, int.parse) - let to_dict = - dests - |> list.zip(dists) - |> dict.from_list() - - dict.insert(acc, from, to_dict) - }) -} - -fn dist_between(leg: #(String, String), dict: Atlas) -> Int { - let assert Ok(dist) = - dict - |> dict.get(leg.0) - |> result.try(dict.get(_, leg.1)) - - dist -} - -fn find_total_distance(row: String, atlas: Atlas) { - let assert [_, route] = string.split(row, ": ") - - string.split(route, " -> ") - |> list.window_by_2 - |> list.fold(0, fn(acc, leg) { acc + dist_between(leg, atlas) }) -} diff --git a/codingquest2024/src/day6/solution.gleam b/codingquest2024/src/day6/solution.gleam deleted file mode 100644 index 8a0319c..0000000 --- a/codingquest2024/src/day6/solution.gleam +++ /dev/null @@ -1,86 +0,0 @@ -import gleam/dict -import gleam/io -import gleam/list -import gleam/regex -import gleam/result -import gleam/string -import simplifile - -pub opaque type Location { - Location(row: Int, col: Int) -} - -pub fn main() { - let assert Ok(data) = simplifile.read(from: "./src/day6/data.txt") - let assert ["Cipher key: " <> key, "Message: " <> raw_message] = - string.split(data, "\n") - - let letter_to_location = make_cipher_grid(key) - let location_to_letter = - letter_to_location - |> dict.fold(dict.new(), fn(acc, k, v) { dict.insert(acc, v, k) }) - - raw_message - |> string.split(" ") - |> list.map(fn(s) { - s - |> string.to_graphemes - |> list.sized_chunk(2) - }) - |> list.map(fn(s) { - s - |> decode_word(letter_to_location, location_to_letter) - |> string.concat() - }) - |> string.join(" ") - |> io.println() -} - -fn make_cipher_grid(key) { - let assert Ok(in_key) = regex.from_string("[" <> key <> "]") - let grid = - regex.split(in_key, "abcdefghiklmnopqrstuvwxyz") - |> string.concat - |> string.append(key, _) - |> string.to_graphemes - |> list.sized_chunk(5) - - list.index_map(grid, fn(row, r) { - list.index_map(row, fn(cell, c) { #(cell, Location(r, c)) }) - }) - |> list.flatten - |> dict.from_list -} - -fn decode_word(word: List(List(String)), to_loc, to_letter) { - case word { - [] -> [] - [[a, b], ..rest] -> [ - transform_pair(a, b, to_loc, to_letter), - ..decode_word(rest, to_loc, to_letter) - ] - _ -> panic as "bad playfair format" - } -} - -fn transform_pair(a, b, to_loc, to_letter) { - let assert Ok(Location(r_a, c_a)) = dict.get(to_loc, a) - let assert Ok(Location(r_b, c_b)) = dict.get(to_loc, b) - - case r_a == r_b, c_a == c_b { - True, _ -> [ - dict.get(to_letter, Location(r_a, { c_a + 4 } % 5)), - dict.get(to_letter, Location(r_b, { c_b + 4 } % 5)), - ] - _, True -> [ - dict.get(to_letter, Location({ r_a + 4 } % 5, c_a)), - dict.get(to_letter, Location({ r_b + 4 } % 5, c_b)), - ] - _, _ -> [ - dict.get(to_letter, Location(r_a, c_b)), - dict.get(to_letter, Location(r_b, c_a)), - ] - } - |> result.values - |> string.concat -} diff --git a/codingquest2024/src/day7/solution.gleam b/codingquest2024/src/day7/solution.gleam deleted file mode 100644 index 2ca7cbf..0000000 --- a/codingquest2024/src/day7/solution.gleam +++ /dev/null @@ -1,73 +0,0 @@ -import gleam/io -import gleam/string -import gleam/set.{type Set} -import gleam/int -import gleam/regex.{Match} -import gleam/option.{Some} -import simplifile - -pub opaque type Item { - File(name: String, size: Int) - Directory(name: String) -} - -pub fn main() { - let assert Ok(data) = simplifile.read(from: "./src/day7/data.txt") - let lines = string.split(data, "\n") - - mark_for_deletion(lines, 0, "", set.new()) - |> io.debug() -} - -fn mark_for_deletion( - lines: List(String), - deleted: Int, - current_folder: String, - deleted_folders: Set(String), -) { - case lines { - [] -> deleted - ["Folder: " <> folder, ..rest] -> - mark_for_deletion(rest, deleted, folder, deleted_folders) - [file, ..rest] -> { - case - string.contains(file, "temporary") - || string.contains(file, "delete") - || set.contains(deleted_folders, current_folder) - { - True -> - case string.contains(file, "[FOLDER") { - True -> { - file - |> get_folder_number() - |> set.insert(deleted_folders, _) - |> mark_for_deletion(rest, deleted, current_folder, _) - } - False -> { - file - |> get_file_size() - |> int.add(deleted, _) - |> mark_for_deletion(rest, _, current_folder, deleted_folders) - } - } - False -> - mark_for_deletion(rest, deleted, current_folder, deleted_folders) - } - } - } -} - -fn get_folder_number(file) { - let assert Ok(re) = regex.from_string("\\[FOLDER ([0-9]+)\\]") - - let assert [Match(submatches: [Some(n)], ..)] = regex.scan(re, file) - n -} - -fn get_file_size(file) { - let assert Ok(re) = regex.from_string("- .+ ([0-9]+)$") - - let assert [Match(submatches: [Some(n)], ..)] = regex.scan(re, file) - let assert Ok(n) = int.parse(n) - n -} diff --git a/codingquest2024/src/day8/solution.gleam b/codingquest2024/src/day8/solution.gleam deleted file mode 100644 index cb4d907..0000000 --- a/codingquest2024/src/day8/solution.gleam +++ /dev/null @@ -1,26 +0,0 @@ -import gleam/io -import gleam/int -import gleam/list -import utilities/memo - -const options = [40, 12, 2, 1] - -const distance = 856 - -pub fn main() { - use cache <- memo.create() - solve(distance, cache) - |> io.debug -} - -fn solve(target, cache) { - use <- memo.memoize(cache, target) - case target { - 0 -> 1 - _ -> - options - |> list.filter(fn(n) { n <= target }) - |> list.map(fn(n) { solve(target - n, cache) }) - |> int.sum - } -} diff --git a/codingquest2024/src/day9/input.txt b/codingquest2024/src/day9/input.txt deleted file mode 100644 index 1d30914..0000000 --- a/codingquest2024/src/day9/input.txt +++ /dev/null @@ -1,203 +0,0 @@ -##################################################################################################### -......#.....#.#.......#.....#.....#.....#.#.................#...............#.............#.#.......# -#.###.#.#.#.#.#.#####.#.#.#.#.###.###.#.#.#.#######.#######.#########.#####.#.#####.#####.#.#.###.#.# -#.#.#...#.#.#.#.#...#...#.#...#.#.....#.#.#...#...#.#...#.....#.....#.#...#.#.#...#.#...#.#...#...#.# -#.#.#####.#.#.#.#.#.#####.#####.#######.#.###.#.#.#.#.#.#.###.#.###.#.#.#.#.#.#.#.#.#.#.#.#.###.##### -#...#$..#.$...#$#.#.$.#.#$..#.$....$..#.$....$#.#.$.#.#$..#.$.#.#$..#.$.#..$..#.$...#$#.#.$.#.#$#...# -###.#.#.#.#####.#.###.#.###.#.#######.#.###.###.#####.#####.###.#.###.###.###.#.#####.###.#.#.#.#.#.# -#.#...#.#.......#...#...#...#.......#.#...#...#...#...#...#.....#.#.....#.#...#.#.....#...#...#.#.#.# -#.#####.#########.#.#####.#.#########.###.#######.#.#.#.#.#######.###.#.###.###.#.#.#.#.#####.#.#.#.# -#.....#.#.#.......#...#...#.#...#...#...#...#.....#.#.#.#.#.....#...#.#...#.#.#.#.#.#.#.....#.#...#.# -#.###$#.#.$.###$####$.#.#$###.$.#.#$#.#.$##.#$####$.#.#$#.#.$.#.#$#.##$##.#$#.#.$##.#$####$.#.#$###.# -#...#...#.......#...#.#.......#.#.#.#.#...#.#.#.....#...#...#.#...#.#...#.....#.#...#.....#.#...#.#.# -###.#####.#####.#.#.#.#########.#.#.#.#.#.#.#.#########.#####.#####.#.#.#######.#.#.#.#.###.###.#.#.# -#.#.#.....#...#.#.#...#...#...#.#.#.#.#.#.#.#.#...#...#...#.#...#...#.#.......#.#.#.#.#...#...#...#.# -#.#.#.#####.#.###.###.#.#.#.#.#.#.#.###.###.#.#.#.#.#.###.#.###.#.###.#######.#.#.#.#.###.###.#####.# -#.#.#$#...$.#..$#.#.$.#.#$#.#.$.#.#$#...$...#$..#.$.#..$#.#.$.#..$#...$.#.#$..#.$.#.#$#.#.$...#$....# -#.#.#.#.#######.#.#.###.#.#.#####.#.#.#.#.#####.#######.#.#.#.#########.#.#.###.#.#.#.#.#.#.###.###.# -#.#.#.#...#...#.#.#.....#.#.#.....#.#.#.#.....#.#.#...#.#...#...........#.#.....#.#.#...#.#...#.#...# -#.#.#####.#.#.#.#.#########.#.#####.#.#.#####.#.#.#.#.#.#################.#######.#.###.#.###.#.###.# -#.#.#.....#.#.....#.......#...#.....#.#.....#.#.#...#.#...#...#...#...#.#.........#.#...#...#.#...#.# -#.#.#$####$.###$###.$####$####$.###$#.##$####$#.##$##.#$#.#.$.#.#$#.#.$.#.#$####$.###$####$.#.#$#.### -#.#.#.#...#.#.......#.#...#...#...#...........#.....#.#...#.#...#...#.#.......#.#.....#...#...#.#...# -#.#.#.###.#.#.#######.#.###.#.###.#######.#########.#.#.###.#########.#######.#.#######.#.#####.###.# -#.#.#...#...#.....#...#.....#...#.......#.#...#.......#.#...#.......#...#...#...#.......#...#.....#.# -#.#.#.#.#.#######.#.###########.###.###.#.#.#.#.#######.#.###.#.#######.#.#.###.#####.#####.#####.#.# -#...#$#.#.$...#$..#.$.#..$#...$...#$#.#.$...#$#...$...#$#...$.#..$....$.#.#$#...$...#$#...$...#$#.#.# -#.#####.###.#.#.###.#.#.#.#.#.###.#.#.#.#####.#####.#.#.###.#########.#.#.#.###.#.#.#.#.#####.#.#.#.# -#.#.....#...#...#...#...#...#...#.#...#.#...#.......#.#...#...#...#...#...#...#...#...#.......#...#.# -#.#.#.#.#.#######.#########.###.#.#####.#.###########.#.#.###.#.#.#.#########.#########.#######.###.# -#.#.#.#.#.......#.#...#...#...#.#.......#...........#.#.#...#.#.#...#.......#...#...#...#.........#.# -#.###$#.##$####$#.#.$.#.#$####$.###$####$.###$###.$##.#$###.$.#.#$###.$####$###.$.###$###.$####$###.# -#.....#.#.....#.#...#.#.#...#.......#...#.......#...#...#.#.#...#.#...#...#.....#...#.#.....#.......# -#######.###.#.#.#######.###.#.#####.#.#.###.#####.#.#####.#.###.###.###.#.#########.#.#.#####.#####.# -#.#.....#...#.#.#.......#.#.#...#.....#.#...#...#.#.#.....#.#...#...#...#...........#.#.#...#.#.#...# -#.#.#####.###.#.#.#######.#.###.#.#####.#####.#.###.#.#.###.#.###.###############.###.#.#.#.#.#.#.### -#.#.#$....$...#$#...$....$#.#.$.#.#$..#.$...#$#...$.#.#$..#.$.#.#$#...$....$....$....$#.#.$...#$..#.# -#.#.#.###.#.###.###.#######.#.#####.#.#####.#.#######.###.#.#.#.#.#.###########.#.#######.#####.###.# -#...#.#.#.#...#.....#.......#.......#.#.......#.....#...#...#.#.#.#.......#.#...#.#.....#...#.......# -#.###.#.#.###########.#####.#####.###.#####.###.###.###.#####.#.#.#######.#.#.###.#.###.#.#.#######.# -#.#.#.#.#.#...#.....#...#...#...#...#.....#.#...#.#...#.#...#.#.#.......#...#.#.....#...#.#.#.....#.# -#.#.#$#.#.$.#.#$###.$##.#$#.#.$.###$###.$.#.#$###.$.###$#.#.$.#.#$###.$####$#.##$####$###.$.#.#$#.#.# -#.#.....#.#.#.#.#...#.#...#.#.#...#...#.#.#.#.....#.#.....#.#...#...#.......#.#...#.#...#.#.#.#...#.# -#.#######.#.#.#.#.#.#.###.###.###.#.#.###.#.#####.#.#.#####.###.#.#.###.#####.#.#.#.###.###.#.#.###.# -#...#.....#.#...#.#...#.#.....#.#...#...#.#.#.....#...#...#...#.#.#...#.#...#.#.#.#...#...#...#.#...# -###.#.#####.#####.###.#.#######.#######.#.###.#########.#.#####.#.###.#.#.#.#.#.#.#.#.###.#.###.#.### -#.#.#$....$...#$..#.$.#..$..#.$....$..#.$.#..$#...$.#.#$#.#.$...#$..#.$.#.#$#...$.#.#$..#.$.#..$#.#.# -#.#.#.###.###.#.###.#.###.#.#.###.###.#.#.#.###.###.#.#.#.#.#######.#.###.#.#####.#.#####.#.#.###.#.# -#.#.#...#...#.#.#...#.....#.#.#...#...#.#...#...#...#.#.#...#.....#.#.....#.......#.......#.#.#.....# -#.#.#####.#.#.#.#.#########.#.#.#####.#.###.#.#####.#.#.#####.#####.###############.#########.#####.# -#.#.....#.#.#.#.#.......#...#.#.....#.#...#...#...#...#...#.....#...#.....................#...#...#.# -#.###$#.#.$##.#$###.$.###$###.$####$#.##$.###$#.#.$##.#$#.#.$##.#$###.$####$###.$####$###.$.#.#$#.#.# -#.....#.#.....#...#.#.#...#.#.#...#.#...#.......#...#...#.#.#...#.#.....#.....#...#.....#.#.#.#.#.#.# -#.#.###.#####.###.#.#.#.###.#.#.#.#.###.###########.###.#.###.###.#####.#.###.#####.###.#.#.###.#.### -#.#...#.#...#.#...#.#.#.#.....#.#.#...#...#...#...#...#.#...#...#.....#.#...#.........#.#...#...#...# -#.###.#.#.#.###.###.#.#.#####.#.#####.###.#.#.###.###.#.###.###.#####.###.###########.#.#####.#####.# -#.#.#$..#.$....$#...$.#.#$..#.$....$#...$...#$....$...#$..#.$.#..$#.#.$...#$....$...#$#.#.$...#$....# -#.#.#####.#######.###.#.#.#.#.#####.###.#########.#.#########.#.#.#.#######.#######.#.#.#.#####.###.# -#.#.....#.....#.#.#.#...#.#.#.#.....#.#.#.......#.#.#...#...#.#.#.......#...#.....#.#.#.....#...#.#.# -#.###.#.#####.#.#.#.#####.#.#.#.###.#.#.###.#####.#.#.#.#.#.#.#.#########.###.###.#.#########.###.#.# -#.....#.......#.#.......#.#.#...#.#.#.#...#.#.....#.#.#.#.#.#.#...#.......#...#...#...........#.#...# -#####$####$####$####$##.#$#.##$##.#$#.##$.#.#$####$.#.#$#.#.$.###$#.##$####$####$####$####$####$#.### -#.........#...#.......#...#.#.....#.....#.#.#.#...#...#...#...#...#...#.....#...................#.#.# -#.#.#####.#.#.#.#.#########.#.#.#####.###.#.#.#.#.###########.#.#####.#.#####.###.#############.#.#.# -#.#...#.#...#.#.#.#...#.....#.#.....#.#...#...#.#.......#...#...#.....#...#...#.#.....#...#...#.#...# -#####.#.#####.#.#.#.#.#.#########.#.#.#.#######.#######.#.#.#####.#######.#.###.#####.#.#.#.#.#####.# -#....$#.#.$.#..$#.#.$...#$....$...#$#.#.$....$..#.$....$#.#.$....$#...$...#$....$...#$..#.$.#..$..#.# -#.#####.#.#.#####.#.#####.###.#######.#.#.#######.#.#####.#########.#####.#####.#.#.#####.#.###.###.# -#.#.......#.....#.#.#...#.#.#.....#...#.#.#.......#.......#.............#...#...#.#.....#.#...#...#.# -#.###.#####.#####.#.###.#.#.#####.#.###.#.#.###.#.#########.###########.#####.#######.#.#.#.#####.#.# -#...#...#.#.......#...#.#...#...#.....#.#.#...#.#.....#...#.#.........#.#.....#.......#.#.#.#.....#.# -#.#.#$#.#.$####$####$.#.#$###.$.#.#$###.$.###$#.##$##.#$#.#.$.###$###.$.#.#$###.$####$#.#.$.#.#$#.#.# -#.#.#...#...........#.#...#...#.#...#...#.#...#.....#...#.#.#.......#.#...#.....#...#...#...#.#...#.# -#.#.#.###.#.#####.###.#.###.###.#####.#.#.#########.###.###.#######.###.###.###.#.#.#.#######.#.###.# -#.#.#.....#.#...#.#...#...#...#.#.....#.#.#.....#...#...#...#...#.#.#...#...#.#.#.#.#.....#...#.#...# -#.#.#########.#.###.#########.#.#.#####.#.#.###.#.#######.###.#.#.#.#.###.###.#.#.#.#######.#####.### -#.#..$#...$...#$....$....$#...$.#.#$..#.$...#$..#.$....$..#.$.#..$#.#.$.#..$#...$.#..$....$....$#...# -#####.#.#############.###.#.###.#.#.#.#.#####.###.#.#########.###.#.###.#.#.#.###.#######.#.###.###.# -#.....#.#...#.........#.#.#...#.#...#.#...#...#...#.....#.....#...#...#.#.#.#.#.#.#...#...#.#.....#.# -#.#.###.#.#.#.#########.#.#.#.#.#####.###.#.###.#######.#.###########.#.###.#.#.#.###.#.#####.#####.# -#.#.#.....#...#.........#.#.#.#.......#...#.#.........#.#...#.........#.....#...#...#.#.#.....#...#.# -#.###$####$####$####$##.#$###.$####$####$##.#$####$##.#$###.$.###$####$##.#$####$.#.#$#.#.$####$#.#.# -#.....#.......#.#.....#.#.....#.....#.......#.#.......#...#.#.....#.#.....#.....#.#.#...#.#...#.#...# -#######.#####.#.#.###.#####.#####.###.#######.#.#########.#.#####.#.#.#####.###.#.#.#.###.#.#.#.##### -#...#...#...#.#.#...#.......#...#.#...#.......#...........#.....#.#...#.....#.#.#.#.#...#.#.#.#...#.# -#.#.#.###.###.#.###.#########.#.###.#########.#############.#.#.#.#.###.#####.#.#.#.###.#.#.#.###.#.# -#.#..$#...$...#$#...$.#..$#...$...#$#...$...#$....$.#..$..#.$.#.#$#.#.$.#..$#...$.#.#$#.#.$.#..$..#.# -#.#####.#.#.###.#.#####.#.#.#####.#.#.#####.#######.#.###.###.#.#.###.###.#.#.#####.#.#.#.#.#######.# -#...#.#.#...#.....#.#...#.#.#...#.#...#.....#.......#...#.#...#.#...#.#...#.#.......#...#.#.#.....#.# -#.#.#.#.#########.#.#.###.#.#.#.#.#####.#####.#######.#.#.#.#######.#.#.###.###.#####.###.#.###.#.#.# -#.#.#.#.#.......#...#.#.#...#.#...#.....#...#.....#...#.#...#...#...#.....#...#.#...#.....#.....#.#.# -###.#$#.#.$####$####$.#.#$###.$####$####$.#.#$###.$.###$####$.#.#$###.$####$#.##$.#.#$####$####$#.#.# -#...#.....#.........#.#.#.....#...#.#.#...#...#...#...#.#.....#...#.#.#.....#.#...#.#.........#...#.# -#.#########.#######.#.#.#.#####.#.#.#.#.###.###.#######.#.#########.#.#.###.#.#.###.#.#####.#.#.###.# -#.......#...#.....#...#...#.....#...#.#.#.#.........#...#.....#.......#.#.#...#.#...#.#.#...#.#.#...# -#.#####.#####.###.#####.#######.#####.#.#.#########.#.#######.###.#####.#.###.#.#.#.#.#.#.#####.#.#.# -#...#$..#.$...#$..#.$.#..$..#.$.#..$....$...#$..#.$...#$....$...#$#.#.$.#..$..#.$.#.#$#.#.$....$#.#.# -###.#.#.#.#####.###.#.#####.#.###########.###.#.#####.#.#.#####.#.#.#.###.#####.#.###.#.#########.#.# -#...#.#.#.#...#.....#.....#...#.....#...#.#...#.#...#.#.#...#...#.#.....#.#.....#...#.#.......#...#.# -#.###.###.#.#.###########.#####.###.#.#.###.###.#.#.###.###.#.###.#.###.###.#######.#.#.#####.#.###.# -#...#.......#...........#.........#...#.......#...#.......#...#.......#...........#.....#.......#.... -##################################################################################################### - -##################################################################################################### -#.........#.........#...#.................#.....#.....#...........#...#.....#.....#...........#.....# -#####.###.#.#######.#.#.###.###.###########.#.###.#.#.###.#######.###.#.#.#.###.#.#####.#.###.#.###.# -#.....#...#.......#...#...#...#.#...#.......#.....#.#...#.....#.#...#...#.#...#.#.....#.#.#.....#.#.# -#.###############.#######.###.###.#.#.#############.###.#####.#.###.#####.###.#.#####.#.#.#.#####.#.# -#....$#...$....$#...$.#.#$..#.$...#$..#.$...#$....$...#$..#.$.#..$#...$...#$..#.$....$#.#.$.#..$#.#.# -#####.#.#.#.###.#####.#.###.#.#############.#.###.#.#.###.#.###.###.#######.###.#.#####.#.###.#.#.#.# -#.....#.#.....#.....#.#...#.#.....#.........#...#.#.#.#...#.#.......#...#.......#...#...#.#...#.#...# -#.###.#.#########.###.###.#.#####.###.###.#####.#.###.#.###.###.#####.#.###########.#####.#.###.#.### -#.#...#...#.....#.#.......#.....#.....#...#.....#...#.#...#...#.#.....#.#.........#.....#...#...#.#.# -#.###$###.$.###$#.#.$####$####$.###$###.$##.#$####$.#.#$#.##$.###$####$.#.#$####$.###$#.##$##.#$#.#.# -#...#.....#.#...#...#...#.....#.#.#.......#.#.....#...#.#...#...#...#...#.#...#.#.#.....#.....#...#.# -#.#.#.#####.###.###.#.#.#.###.#.#.#.#######.#.###.#####.###.#.#.###.#.#.#.#.#.#.#.###.###.#####.###.# -#.#.#.....#...#...#.#.#.....#...#...#.......#...#...#...#...#.#.#...#.#.#.#.#...#...#.#.....#.#.#...# -#.#.#.#######.###.###.#####.#####.###.#########.#.#.#.###.#####.#.###.#.#.#.#######.#.#####.#.#.#.#.# -#.#..$#...$...#$#...$.#..$#...$...#$..#.$....$..#.$...#$..#.$...#$..#.$.#..$#...$...#$#...$.#.#$#.#.# -#.#####.#######.#######.#.#.#.#.###.###.#########.#.###.###.#######.#.#####.#.#######.#.#.#.#.#.#.#.# -#.#...#.....#.......#...#.#.#...#...#...#.......#.#.#.......#.......#.....#.#.....#.....#...#.#...#.# -#.#.#.#####.#.###.#.###.#.#.#####.#####.###.###.#.###.#######.#########.###.#####.###########.#####.# -#...#.#...#.#.#...#...#.#...#...#.....#...#...#.#.....#.#...#...#.#...#...#.#...#.......#...#.....#.# -#####$#.#.$.###$####$.###$###.$.###$#.##$.###$#.##$####$#.#.$##.#$#.#.$##.#$#.##$##.#$#.#.$##.#$###.# -#.....#.#.#...#.#...#.........#.....#.#.#.....#.........#.#.....#...#...#.#.#.#...#...#.#...#.#.....# -#.#####.#.###.#.#.#.###############.#.#.#####.#.#######.#.###########.#.#.#.#.#.#.#####.###.#.####### -#.#.#...#...#.#...#...#...#.......#...#.....#...#.#.....#.............#.#...#.#.#.#.....#...#.....#.# -#.#.#.#####.#.#####.#.#.#.#######.#####.###.#####.#.###############.#######.#.#.#.#.#####.#.#####.#.# -#.#.#$..#.$...#$..#.$.#.#$....$.#..$..#.$.#..$#...$.#..$....$....$#.#.$...#$..#.$...#$....$...#$..#.# -#.#.###.#.#####.#.###.#.#######.#.###.###.#.###.#####.#######.#####.#.###.#####.#####.#.#######.###.# -#.#...#.#.#.....#...#...#.......#...#.....#.#...#.....#.....#.......#.#...#...#...#...#...#...#.#...# -#.#.#.#.###.#######.#####.#.#######.#####.#.#.###.#####.#############.###.#.#.###.###.###.#.#.#.###.# -#.#.#.#.....#.#.....#.....#.#.....#.#...#.#...#...#...........#.#...#...#...#...#...#...#...#.#.#...# -#.#.#$####$##.#$####$.###$#.#.$##.#$#.#.$####$#.##$####$###.$.#.#$#.##$.###$###.$##.#$#.##$##.#$#.#.# -#.#.#...#.....#.....#...#.#.#.#.#.....#.#...#.#...........#.#.#...#...#.#...........#.#.....#...#.#.# -#.#####.#.#.#.#####.#.#.#.#.#.#.#######.#.#.###.#########.#.#.#####.#.#.#############.#####.#####.### -#.....#...#.#.....#.#.#.#...#.#.......#.#.#...#.#.......#.#.#.......#.#...........#.......#.....#...# -#####.#.###.#.#######.#.###.#.#.#####.#.#.###.#.#.#####.#.#.#######.#####.#######.#.###.#.#####.#.#.# -#...#$#...$.#.#$....$.#..$#.#.$....$#.#.$.#.#$..#.$...#$#...$....$#.#.$.#..$....$.#.#$..#.$...#$#.#.# -###.#.###.#.###.###.#.###.###.#######.#.#.#.#####.#.###.#######.###.#.#.#######.#.###.#.###.#.#.###.# -#...#.#...#...#...#.#...#...#.#.......#...#.....#.#.....#.......#...#.#.#.....#.#.#...#.#...#.#.#...# -#.#.#.#.#####.###.#.###.###.#.#.#####.#######.###.#.#####.#####.#.###.#.#####.#.#.#.#####.###.#.#.### -#.#.#.#.#...#.....#.....#.#...#.....#.........#...#...#.......#.#.#.#.#.....#...#.#.........#.#.#...# -#.###$#.#.$.###$####$##.#$####$####$###.$####$#.##$##.#$####$.###$#.#.$####$#.##$.#.#$####$##.#$###.# -#.....#...#.#.........#.....#.....#...#.#.......#...#.#.....#.......#.....#.#...#.#...#.....#...#...# -#.#######.###.#.#####.#####.#.###.#.#.###.#######.#.#.###################.#.###.#.#####.###.###.#.#.# -#...#...#.#...#.#...#.....#...#...#.#...#.......#.#.#.......#.......#.....#.#...#.....#.#.#...#...#.# -###.###.#.#.#####.#.#####.#######.#####.#######.#.#.#######.#.#####.#.#####.#.#######.#.#.###.####### -#.#..$#.#.$....$..#.$...#$....$.#..$#...$...#$..#.$....$#...$....$#...$....$#...$....$#.#.$.#.#$....# -#.###.#.#.#########.#.#########.###.#.#.#####.###.#.#####.#######.#####.#####.###.#####.###.#.#.###.# -#.....#.#...#...#...#...#.....#.#.....#.#.....#.#.#...............#...#...#...#...#.........#...#...# -#.#####.###.###.#.###.#.#.#.###.#########.#####.#.###################.###.###.#.###.#######.#####.### -#.#.......#.....#.#...#...#...#.#.......#.#...#...#.......#...........#.#...#.#.......#...#.....#...# -#.###$###.$.###$#.#.$####$#.#.$.#.#$###.$.#.#$####$.###$#.#.$.###$###.$.###$#.##$####$#.#.$.###$#.#.# -#.#...#...#.#.....#...#.....#.#...#...#.#.#.#.....#.....#.#.#.#...#.#...#...#...#...#...#.#.#...#.#.# -#.#.#.#.###.#.#######.#.#####.#####.###.#.#.#####.#####.#.###.#.#.#.#.###.#####.#.###.###.#.#.#.###.# -#...#.#.....#.#.....#...#...#.......#...#...#...#.....#.#...#.#.#.#...#...#...#.#...#.#.#.#.#.#.#...# -#####.#.#####.#.###.#.###.#.#####.#.#.###.###.#.#.#.###.###.#.#.#.###.#.###.###.###.#.#.#.#.#.#.#.#.# -#...#$#...$...#$..#.$.#..$#...$...#$#.#.$...#$#.#.$.#..$#.#.$...#$..#.$.#..$....$...#$#...$.#.#$..#.# -#.###.###.#.###########.###########.#.#####.#.#.###.#.###.#.#.#####.###.#.#########.#.#.###.#.#####.# -#...#...#.#.#...........#...#...#...#...#...#.#...#.#.....#.#.....#.....#.#...#.....#.#...#.#.#.#...# -#.#.###.###.#.#.#########.#.#.#.#.#####.#####.###.#.###.###.#####.#######.#.#.#######.###.###.#.#.### -#.#...#.....#.#.......#...#...#.#.#...#.......#...#...#.#...#...#...#.....#.#...#.....#.#...#.#...#.# -#.###$####$##.#$####$.#.#$####$.#.#$#.##$####$#.##$.#.#$#.##$.###$#.##$####$###.$.###$#.##$.#.#$###.# -#...#...#.#...#.....#...#.....#.#.............#.#.#.#...#.#...#...#...#.....#.#.#.....#...#...#...#.# -#.###.#.#.#.###.#####.#######.#.#############.#.#.#.#####.#.###.#.###.#.#####.#.#####.#.#.#######.#.# -#.#...#.#.#.#...#...#...#.....#.#...........#.#.#...#...#.#.....#...#...#...#.#.......#.#.......#.#.# -###.###.#.#.#.#.#.#.###.#.###.#.#.###########.#.###.#.#.#.###.#####.###.#.#.#.#########.#######.#.#.# -#...#$#.#.$.#.#$#.#.$...#$..#.$.#..$....$...#$#...$.#.#$#...$....$#...$...#$#...$....$....$....$#...# -#.###.#.#.#.#.###.#.###.###.#.#.#########.#.#.###.###.#.###.#####.###.#####.###.#.#########.###.###.# -#.#...#.#.#.#.#...#...#.....#.#.....#...#.#...#...#...#.....#.....#...#.......#.......#.....#.....#.# -#.#.###.#.#.#.#.#####.#######.#####.#.#.#######.###.#########.#####.#########.#######.#.#####.#####.# -#.#.....#.....#...#.........#.....#.#.#.......#.....#.........#.....#...#...#.......#.#.....#.#.....# -#.###$####$##.#$#.##$####$#.##$##.#$#.##$####$####$####$####$##.#$###.$.#.#$####$##.#$#.##$.###$##### -#...#.......#...#.......#.......#.#.#.#.....#.........#...#...#.......#...#.#.....#...#.#.#...#.#...# -#.#.#######.#.#####.###.#.#####.###.#.###.#####.#####.#####.#.#######.#####.#####.###.#.#.###.#.#.#.# -#.#.......#.#.#...#.#...#.#...#.....#...#.#.....#...#...#...#.#.....#.....#.#...#...#.#...#...#...#.# -#.#######.#.#.#.#.###.#.###.#.#####.###.#.#.#######.###.#.###.#.###.#######.#.#.###.#.###.#.#.#####.# -#...#$..#.$.#.#$#...$.#.#$..#.$....$#.#.$.#..$....$....$#.#.$.#..$#...$....$#.#.$.#..$#...$.#.#$....# -###.###.#.#.#.#.###.#.###.###.###.###.#.#.#######.###.###.#.#.#.#.#####.#####.###.#.#######.###.##### -#.....#.#.#.#.#...#.#.#...#.#...#.#...#.#...#...#...#.#...#...#.#.#...#.#.....#.#.#.......#...#.#...# -#####.#.#.#.#.###.#.#.#.###.###.#.#.###.#.#.#.#.###.#.#.#.#.###.#.#.#.###.#####.#.#######.###.#.#.#.# -#...#...#.#.#.#...#...#.#.....#.#.#...#...#.#.#.#...#.#.#.#...#.#...#...........#.......#.#...#.#.#.# -#.#.#$###.$.###$####$.#.#$###.$.#.#$#.##$##.#$###.$##.#$#.##$.###$###.$####$####$##.#$#.#.$.#.#$#.#.# -#.#.......#.....#.#...#.#.#...#.#.....#...#.#.#...#.......#...#.....#.#.#.....#...#.#...#...#.#.#.#.# -#.#####.#########.#.###.###.#.#.###.###.#.#.#.#.###########.###.###.#.#.#.###.#.#.###.#######.#.#.#.# -#.#...#.#...#...#...#.#.#...#.#.#...#...#...#.#.....#.....#...#.#...#...#.#.....#.#...#.......#...#.# -#.#.#.###.#.#.#.#.###.#.#.###.#.###.#.#######.#####.#.#.#.###.#.###.###.#.#######.#.###.###########.# -#.#.#$....$.#.#$#.#.$.#..$#...$...#$#.#.$....$#...$.#.#$#.#.$.#..$#...$.#..$#...$...#$#.#.$...#$....# -#.#.#######.#.###.#.#.#####.#####.###.#.#.###.#.#.#.###.###.#####.###.#####.#########.#.#.###.#.##### -#...#.......#.....#.#.#.....#...#.#...#.#.#.#...#.#...#...#.......#.........#...#.......#.#.#.#.....# -#.###.###########.#.###.#######.#.#.#####.#.#####.###.#.#.#########.#########.#.#######.#.#.#.#####.# -#.#.#.......#...#...#...#.....#.#.#.#.....#.#.....#.#.#.#...........#.........#.#.....#.#...#.....#.# -#.#.#$####$.#.#$###.$.###$###.$.#.#$#.##$##.#$####$.#.#$####$##.#$###.$####$###.$.###$###.$##.#$###.# -#.#.......#...#.#.#.#.#...#.....#.#.#.#...#...#.....#.........#...#.#.#...#.....#...#.....#...#.....# -#.#.###.#######.#.#.#.###.#######.#.#.#.#.#.###.###.#########.###.#.#.###.#.#######.#######.#.#.##### -#...#...#...#...#...#...#.#.....#.#...#.#.#...#.#.......#...#...#...#.#...#.#.......#.......#.#.....# -###.#####.#.#.###.#####.#.#.###.#.#####.#####.#.#######.#.#.#.#.#####.#.###.#.#######.#############.# -#...#$..#.$...#$....$...#$#.#.$...#$..#.$....$#...$.#..$#.#.$.#..$..#.$.#..$#.#.$...#$..#.$...#$....# -#.###.#.#.#########.#.###.#.#######.#.#.#######.#.#.#.###.#.#######.###.#.###.#.###.###.#.###.#.##### -#...#.#...#...#...#.#...#.#.#.......#...#.....#.#.#.#.#.#.#.........#...#...#.#...#.#.#.#.#...#.#...# -###.#.#####.#.#.#.#####.#.#.#.###########.###.###.#.#.#.#.###########.#####.#.###.#.#.#.#.#.###.#.#.# -#...#.......#...#.........#...............#.......#.....#.................#.......#...#...#.......#.# -##################################################################################################### \ No newline at end of file diff --git a/codingquest2024/src/day9/solution.gleam b/codingquest2024/src/day9/solution.gleam deleted file mode 100644 index e69de29..0000000 diff --git a/codingquest2024/src/utilities/memo.gleam b/codingquest2024/src/utilities/memo.gleam deleted file mode 100644 index b06d8fd..0000000 --- a/codingquest2024/src/utilities/memo.gleam +++ /dev/null @@ -1,57 +0,0 @@ -import gleam/dict.{type Dict} -import gleam/otp/actor.{type Next, Continue, Stop} -import gleam/erlang/process.{type Subject, Normal} -import gleam/option.{None} - -const timeout = 1000 - -type Message(k, v) { - Shutdown - Get(key: k, client: Subject(Result(v, Nil))) - Set(key: k, value: v) -} - -type Server(k, v) = - Subject(Message(k, v)) - -pub opaque type Cache(k, v) { - Cache(server: Server(k, v)) -} - -fn handle_message( - message: Message(k, v), - dict: Dict(k, v), -) -> Next(Message(k, v), Dict(k, v)) { - case message { - Shutdown -> Stop(Normal) - Get(key, client) -> { - process.send(client, dict.get(dict, key)) - Continue(dict, None) - } - Set(key, value) -> Continue(dict.insert(dict, key, value), None) - } -} - -pub fn create(apply fun: fn(Cache(k, v)) -> t) -> t { - let assert Ok(server) = actor.start(dict.new(), handle_message) - let result = fun(Cache(server)) - process.send(server, Shutdown) - result -} - -pub fn set(in cache: Cache(k, v), for key: k, insert value: v) -> Nil { - process.send(cache.server, Set(key, value)) -} - -pub fn get(from cache: Cache(k, v), fetch key: k) -> Result(v, Nil) { - process.call(cache.server, fn(c) { Get(key, c) }, timeout) -} - -pub fn memoize(with cache: Cache(k, v), this key: k, apply fun: fn() -> v) -> v { - let result = case get(from: cache, fetch: key) { - Ok(value) -> value - Error(Nil) -> fun() - } - set(in: cache, for: key, insert: result) - result -} diff --git a/codingquest2024/test/codingquest2024_test.gleam b/codingquest2024/test/codingquest2024_test.gleam deleted file mode 100644 index 3831e7a..0000000 --- a/codingquest2024/test/codingquest2024_test.gleam +++ /dev/null @@ -1,12 +0,0 @@ -import gleeunit -import gleeunit/should - -pub fn main() { - gleeunit.main() -} - -// gleeunit test functions end in `_test` -pub fn hello_world_test() { - 1 - |> should.equal(1) -} diff --git a/gleam/aoc2017/.github/workflows/test.yml b/gleam/aoc2017/.github/workflows/test.yml new file mode 100644 index 0000000..664c44a --- /dev/null +++ b/gleam/aoc2017/.github/workflows/test.yml @@ -0,0 +1,23 @@ +name: test + +on: + push: + branches: + - master + - main + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: erlef/setup-beam@v1 + with: + otp-version: "26.0.2" + gleam-version: "1.2.0-rc1" + rebar3-version: "3" + # elixir-version: "1.15.4" + - run: gleam deps download + - run: gleam test + - run: gleam format --check src test diff --git a/gleam/aoc2017/.gitignore b/gleam/aoc2017/.gitignore new file mode 100644 index 0000000..599be4e --- /dev/null +++ b/gleam/aoc2017/.gitignore @@ -0,0 +1,4 @@ +*.beam +*.ez +/build +erl_crash.dump diff --git a/gleam/aoc2017/README.md b/gleam/aoc2017/README.md new file mode 100644 index 0000000..9714473 --- /dev/null +++ b/gleam/aoc2017/README.md @@ -0,0 +1,25 @@ +# aoc2017_gleam + +[![Package Version](https://img.shields.io/hexpm/v/aoc2017_gleam)](https://hex.pm/packages/aoc2017_gleam) +[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/aoc2017_gleam/) + +```sh +gleam add aoc2017_gleam +``` +```gleam +import aoc2017_gleam + +pub fn main() { + // TODO: An example of the project in use +} +``` + +Further documentation can be found at . + +## Development + +```sh +gleam run # Run the project +gleam test # Run the tests +gleam shell # Run an Erlang shell +``` diff --git a/gleam/aoc2017/gleam.toml b/gleam/aoc2017/gleam.toml new file mode 100644 index 0000000..e00659c --- /dev/null +++ b/gleam/aoc2017/gleam.toml @@ -0,0 +1,24 @@ +name = "aoc2017_gleam" +version = "1.0.0" + +# Fill out these fields if you intend to generate HTML documentation or publish +# your project to the Hex package manager. +# +# description = "" +# licences = ["Apache-2.0"] +# repository = { type = "github", user = "username", repo = "project" } +# links = [{ title = "Website", href = "https://gleam.run" }] +# +# For a full reference of all the available options, you can have a look at +# https://gleam.run/writing-gleam/gleam-toml/. + +[dependencies] +gleam_stdlib = ">= 0.34.0 and < 2.0.0" +gladvent = ">= 0.7.3 and < 1.0.0" +gary = ">= 1.0.1 and < 2.0.0" +gleam_otp = ">= 0.10.0 and < 1.0.0" +gleam_erlang = ">= 0.25.0 and < 1.0.0" +glearray = ">= 0.2.2 and < 1.0.0" + +[dev-dependencies] +gleeunit = ">= 1.0.0 and < 2.0.0" diff --git a/gleam/aoc2017/manifest.toml b/gleam/aoc2017/manifest.toml new file mode 100644 index 0000000..f48e595 --- /dev/null +++ b/gleam/aoc2017/manifest.toml @@ -0,0 +1,36 @@ +# This file was generated by Gleam +# You typically do not need to edit this file + +packages = [ + { name = "argv", version = "1.0.2", build_tools = ["gleam"], requirements = [], otp_app = "argv", source = "hex", outer_checksum = "BA1FF0929525DEBA1CE67256E5ADF77A7CDDFE729E3E3F57A5BDCAA031DED09D" }, + { name = "filepath", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "EFB6FF65C98B2A16378ABC3EE2B14124168C0CE5201553DE652E2644DCFDB594" }, + { name = "gary", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gary", source = "hex", outer_checksum = "4C05611EEC74876A1E36309EED22C826B92F22E566579ACBC65C579CD615EC60" }, + { name = "gladvent", version = "0.7.3", build_tools = ["gleam"], requirements = ["argv", "filepath", "gleam_erlang", "gleam_json", "gleam_otp", "gleam_package_interface", "gleam_stdlib", "glint", "parallel_map", "shellout", "simplifile", "snag", "spinner", "tom"], otp_app = "gladvent", source = "hex", outer_checksum = "59D93FD759427BCE8EE9828C0B62A3CD18362225F948C9789D334DCEAD621358" }, + { name = "gleam_community_ansi", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_community_colour", "gleam_stdlib"], otp_app = "gleam_community_ansi", source = "hex", outer_checksum = "FE79E08BF97009729259B6357EC058315B6FBB916FAD1C2FF9355115FEB0D3A4" }, + { name = "gleam_community_colour", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib"], otp_app = "gleam_community_colour", source = "hex", outer_checksum = "795964217EBEDB3DA656F5EB8F67D7AD22872EB95182042D3E7AFEF32D3FD2FE" }, + { name = "gleam_erlang", version = "0.25.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "054D571A7092D2A9727B3E5D183B7507DAB0DA41556EC9133606F09C15497373" }, + { name = "gleam_json", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "thoas"], otp_app = "gleam_json", source = "hex", outer_checksum = "9063D14D25406326C0255BDA0021541E797D8A7A12573D849462CAFED459F6EB" }, + { name = "gleam_otp", version = "0.10.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "0B04FE915ACECE539B317F9652CAADBBC0F000184D586AAAF2D94C100945D72B" }, + { name = "gleam_package_interface", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib"], otp_app = "gleam_package_interface", source = "hex", outer_checksum = "52A721BCA972C8099BB881195D821AAA64B9F2655BECC102165D5A1097731F01" }, + { name = "gleam_stdlib", version = "0.38.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "663CF11861179AF415A625307447775C09404E752FF99A24E2057C835319F1BE" }, + { name = "glearray", version = "0.2.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "glearray", source = "hex", outer_checksum = "9C207E05F38D724F464FA921378DB3ABC2B0A2F5821116D8BC8B2CACC68930D5" }, + { name = "gleeunit", version = "1.1.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "72CDC3D3F719478F26C4E2C5FED3E657AC81EC14A47D2D2DEBB8693CA3220C3B" }, + { name = "glint", version = "1.0.0-rc2", build_tools = ["gleam"], requirements = ["gleam_community_ansi", "gleam_community_colour", "gleam_stdlib", "snag"], otp_app = "glint", source = "hex", outer_checksum = "FD5C47CE237CA67121F3946ADE7C630750BB67F5E8A4717D2DF5B5EE758CCFDB" }, + { name = "parallel_map", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_otp", "gleam_stdlib"], otp_app = "parallel_map", source = "hex", outer_checksum = "653714A9FD63EACD1A9D0A6582A972B0EC109AE275CDDD2E99CFC3DFAFAB9225" }, + { name = "repeatedly", version = "2.1.1", build_tools = ["gleam"], requirements = [], otp_app = "repeatedly", source = "hex", outer_checksum = "38808C3EC382B0CD981336D5879C24ECB37FCB9C1D1BD128F7A80B0F74404D79" }, + { name = "shellout", version = "1.6.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "shellout", source = "hex", outer_checksum = "E2FCD18957F0E9F67E1F497FC9FF57393392F8A9BAEAEA4779541DE7A68DD7E0" }, + { name = "simplifile", version = "1.7.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "1D5DFA3A2F9319EC85825F6ED88B8E449F381B0D55A62F5E61424E748E7DDEB0" }, + { name = "snag", version = "0.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "snag", source = "hex", outer_checksum = "54D32E16E33655346AA3E66CBA7E191DE0A8793D2C05284E3EFB90AD2CE92BCC" }, + { name = "spinner", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_community_ansi", "gleam_erlang", "gleam_stdlib", "glearray", "repeatedly"], otp_app = "spinner", source = "hex", outer_checksum = "200BA3D4A04D468898E63C0D316E23F526E02514BC46454091975CB5BAE41E8F" }, + { name = "thoas", version = "1.2.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "E38697EDFFD6E91BD12CEA41B155115282630075C2A727E7A6B2947F5408B86A" }, + { name = "tom", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "tom", source = "hex", outer_checksum = "A5364613E3DBF77F38EFF81DA9F99324086D029EC2B2D44348762FBE38602311" }, +] + +[requirements] +gary = { version = ">= 1.0.1 and < 2.0.0" } +gladvent = { version = ">= 0.7.3 and < 1.0.0" } +gleam_erlang = { version = ">= 0.25.0 and < 1.0.0" } +gleam_otp = { version = ">= 0.10.0 and < 1.0.0" } +gleam_stdlib = { version = ">= 0.34.0 and < 2.0.0" } +glearray = { version = ">= 0.2.2 and < 1.0.0"} +gleeunit = { version = ">= 1.0.0 and < 2.0.0" } diff --git a/gleam/aoc2017/src/aoc2017_gleam.gleam b/gleam/aoc2017/src/aoc2017_gleam.gleam new file mode 100644 index 0000000..8c2ba67 --- /dev/null +++ b/gleam/aoc2017/src/aoc2017_gleam.gleam @@ -0,0 +1,5 @@ +import gleam/io + +pub fn main() { + io.println("Hello from aoc2017_gleam!") +} diff --git a/gleam/aoc2017/src/aoc_2017/day_1.gleam b/gleam/aoc2017/src/aoc_2017/day_1.gleam new file mode 100644 index 0000000..786d0dd --- /dev/null +++ b/gleam/aoc2017/src/aoc_2017/day_1.gleam @@ -0,0 +1,32 @@ +import gleam/int +import gleam/list +import gleam/result +import gleam/string + +pub fn parse(input: String) { + input + |> string.to_graphemes() + |> list.map(int.parse) + |> result.values() +} + +pub fn pt_1(input: List(Int)) { + pair_by(numbers: input, considering: 1) +} + +pub fn pt_2(input: List(Int)) { + pair_by(numbers: input, considering: list.length(input) / 2) +} + +fn find_neighbor_matches(number_pairs: List(#(Int, Int))) { + case number_pairs { + [] -> 0 + [#(a, b), ..rest] if a == b -> a + find_neighbor_matches(rest) + [_, ..rest] -> find_neighbor_matches(rest) + } +} + +fn pair_by(numbers xs: List(Int), considering by: Int) { + list.zip(xs, list.append(list.drop(xs, by), list.take(xs, by))) + |> find_neighbor_matches() +} diff --git a/gleam/aoc2017/src/aoc_2017/day_10.gleam b/gleam/aoc2017/src/aoc_2017/day_10.gleam new file mode 100644 index 0000000..676e0ee --- /dev/null +++ b/gleam/aoc2017/src/aoc_2017/day_10.gleam @@ -0,0 +1,77 @@ +import gleam/int +import gleam/list +import gleam/result +import gleam/string + +const size = 256 + +const suffix = [17, 31, 73, 47, 23] + +fn parse_as_numbers(input: String) { + input + |> string.split(",") + |> list.map(int.parse) + |> result.values() +} + +fn parse_as_bytes(input: String) { + input + |> string.to_utf_codepoints + |> list.map(string.utf_codepoint_to_int) + |> list.append(suffix) +} + +pub fn pt_1(input: String) { + let twisted = twist(list.range(0, size - 1), parse_as_numbers(input), 0, 0) + + let assert #([first, second, ..], _, _) = twisted + first * second +} + +pub fn pt_2(input: String) { + megatwist(list.range(0, size - 1), parse_as_bytes(input), 0, 0, 64) + |> list.sized_chunk(16) + |> list.map(fold_xor) + |> string.concat() +} + +fn twist(loop: List(Int), lengths: List(Int), skip: Int, index: Int) { + case lengths { + [] -> #(loop, skip, index) + [l, ..ls] -> + loop + |> roll(index) + |> flip(l) + |> roll({ size - index } % size) + |> twist(ls, skip + 1, { index + l + skip } % size) + } +} + +fn megatwist(loop, lengths, skip, index, iterations) { + case iterations { + 0 -> loop + n -> { + let #(next_loop, next_skip, next_index) = + twist(loop, lengths, skip, index) + megatwist(next_loop, lengths, next_skip, next_index, n - 1) + } + } +} + +fn roll(list: List(a), by: Int) { + let #(left, right) = list.split(list, by % size) + list.append(right, left) +} + +fn flip(list: List(a), length: Int) { + let #(left, right) = list.split(list, length) + list.append(list.reverse(left), right) +} + +fn fold_xor(xs: List(Int)) { + let assert Ok(n) = list.reduce(xs, int.bitwise_exclusive_or) + n + |> int.to_base16() + |> string.pad_left(to: 2, with: "0") + |> string.lowercase() +} diff --git a/gleam/aoc2017/src/aoc_2017/day_11.gleam b/gleam/aoc2017/src/aoc_2017/day_11.gleam new file mode 100644 index 0000000..7d3df0b --- /dev/null +++ b/gleam/aoc2017/src/aoc_2017/day_11.gleam @@ -0,0 +1,75 @@ +import gleam/int +import gleam/list +import gleam/string + +pub type Direction { + North + Northeast + Northwest + South + Southeast + Southwest +} + +type HexPosition { + HexPosition(x: Int, y: Int, z: Int) +} + +const start = HexPosition(0, 0, 0) + +fn to_direction(str: String) -> Direction { + case str { + "n" -> North + "ne" -> Northeast + "nw" -> Northwest + "s" -> South + "se" -> Southeast + "sw" -> Southwest + _ -> panic as "unrecognized direction" + } +} + +fn distance(hp1: HexPosition, hp2: HexPosition) -> Int { + { + int.absolute_value(hp1.x - hp2.x) + + int.absolute_value(hp1.y - hp2.y) + + int.absolute_value(hp1.z - hp2.z) + } + / 2 +} + +fn move(p, direction) -> HexPosition { + case direction { + North -> HexPosition(..p, y: p.y + 1, z: p.z - 1) + South -> HexPosition(..p, y: p.y - 1, z: p.z + 1) + Northeast -> HexPosition(..p, x: p.x + 1, z: p.z - 1) + Southwest -> HexPosition(..p, x: p.x - 1, z: p.z + 1) + Southeast -> HexPosition(..p, x: p.x + 1, y: p.y - 1) + Northwest -> HexPosition(..p, x: p.x - 1, y: p.y + 1) + } +} + +pub fn parse(input: String) -> List(Direction) { + input + |> string.split(",") + |> list.map(to_direction) +} + +pub fn pt_1(input: List(Direction)) { + do_walk(input, start, 0).0 +} + +pub fn pt_2(input: List(Direction)) { + do_walk(input, start, 0).1 +} + +fn do_walk(steps, position, max) { + case steps { + [] -> #(distance(position, HexPosition(0, 0, 0)), max) + [next, ..rest] -> { + let new_position = move(position, next) + let new_max = int.max(max, distance(new_position, start)) + do_walk(rest, new_position, new_max) + } + } +} diff --git a/gleam/aoc2017/src/aoc_2017/day_12.gleam b/gleam/aoc2017/src/aoc_2017/day_12.gleam new file mode 100644 index 0000000..a9d73c5 --- /dev/null +++ b/gleam/aoc2017/src/aoc_2017/day_12.gleam @@ -0,0 +1,44 @@ +import gleam/dict +import gleam/list +import gleam/set.{type Set} +import gleam/string + +type Pipes = + dict.Dict(String, List(String)) + +pub fn parse(input: String) -> Pipes { + use acc, row <- list.fold(string.split(input, "\n"), dict.new()) + let assert Ok(#(from, to)) = string.split_once(row, " <-> ") + let to_nodes = string.split(to, ", ") + dict.insert(acc, from, to_nodes) +} + +pub fn pt_1(input: Pipes) { + next_nodes("0", input, set.new()) |> set.size() +} + +pub fn pt_2(input: Pipes) { + count_groups(dict.keys(input), input, 0) +} + +fn next_nodes(current: String, pipes: Pipes, found: Set(String)) { + let assert Ok(to_nodes) = dict.get(pipes, current) + + use acc, node <- list.fold(to_nodes, found) + case set.contains(found, node) { + False -> acc |> set.insert(node) |> next_nodes(node, pipes, _) + True -> acc + } +} + +fn count_groups(all_nodes: List(String), pipes: Pipes, count: Int) { + case all_nodes { + [] -> count + [first, ..] -> { + let next_subgraph = next_nodes(first, pipes, set.new()) + let remaining = + list.filter(all_nodes, fn(n) { !set.contains(next_subgraph, n) }) + count_groups(remaining, pipes, count + 1) + } + } +} diff --git a/gleam/aoc2017/src/aoc_2017/day_13.gleam b/gleam/aoc2017/src/aoc_2017/day_13.gleam new file mode 100644 index 0000000..6b04a77 --- /dev/null +++ b/gleam/aoc2017/src/aoc_2017/day_13.gleam @@ -0,0 +1,58 @@ +import gleam/int +import gleam/list +import gleam/option.{Some} +import gleam/regex.{Match} +import gleam/string + +pub type Layer { + Layer(depth: Int, cycle: Int) +} + +pub type Status { + Caught(severity: Int) + Free +} + +pub fn parse(input: String) { + let assert Ok(re) = regex.from_string("([0-9]+): ([0-9]+)") + + use acc, row <- list.fold(string.split(input, "\n"), []) + let assert [Match(submatches: [Some(depth), Some(cycle)], ..)] = + regex.scan(row, with: re) + let assert Ok(depth) = int.parse(depth) + let assert Ok(cycle) = int.parse(cycle) + [Layer(depth, cycle), ..acc] +} + +fn severity(time: Int, depth: Int, cycle: Int) { + case { time + depth } % { 2 * { cycle - 1 } } { + 0 -> Caught(cycle * depth) + _ -> Free + } +} + +pub fn pt_1(input: List(Layer)) { + use acc, layer <- list.fold(input, 0) + case severity(0, layer.depth, layer.cycle) { + Free -> acc + Caught(severity) -> acc + severity + } +} + +pub fn pt_2(input: List(Layer)) { + find_delay(0, input) +} + +fn find_delay(delay: Int, layers: List(Layer)) { + let trial_run = + list.try_fold(layers, Free, fn(_, layer) { + case severity(delay, layer.depth, layer.cycle) { + Free -> Ok(Free) + Caught(_) -> Error(Nil) + } + }) + case trial_run { + Ok(_) -> delay + _err -> find_delay(delay + 1, layers) + } +} diff --git a/gleam/aoc2017/src/aoc_2017/day_14.gleam b/gleam/aoc2017/src/aoc_2017/day_14.gleam new file mode 100644 index 0000000..2a74912 --- /dev/null +++ b/gleam/aoc2017/src/aoc_2017/day_14.gleam @@ -0,0 +1,82 @@ +import aoc_2017/day_10.{pt_2 as knot} +import gleam/int +import gleam/list +import gleam/result +import gleam/set +import gleam/string +import helpers/set_state + +pub fn pt_1(input: String) { + use acc, row <- list.fold(make_rows(input), 0) + let count = row |> knot() |> popcount() + acc + count +} + +fn make_rows(input: String) { + use row <- list.map(list.range(0, 127)) + input <> "-" <> int.to_string(row) +} + +fn popcount(hex_number: String) -> Int { + let assert Ok(n) = int.base_parse(hex_number, 16) + let assert Ok(digits) = int.digits(n, 2) + + use acc, digit <- list.fold(digits, 0) + case digit { + 1 -> acc + 1 + _ -> acc + } +} + +pub fn pt_2(input: String) { + let grid = set_state.start_actor(make_grid(input)) + + find_next_group(grid, 0) +} + +fn make_grid(input: String) { + let raw_grid = + list.map(make_rows(input), fn(row) { + row + |> knot() + |> int.base_parse(16) + |> result.map(int.to_base2) + |> result.map(string.pad_left(_, with: "0", to: 128)) + |> result.map(string.to_graphemes) + }) + |> result.values + + { + use total_acc, row, i <- list.index_fold(raw_grid, set.new()) + use acc, bit, j <- list.index_fold(row, total_acc) + case bit { + "1" -> set.insert(acc, #(i, j)) + _zero -> acc + } + } +} + +fn find_next_group(actor, count) { + case set_state.pop(actor) { + Ok(p) -> { + list.each(neighbors(p), remove_neighbor(actor, _)) + find_next_group(actor, count + 1) + } + Error(Nil) -> count + } +} + +fn neighbors(of: #(Int, Int)) { + let #(i, j) = of + [#(i + 1, j), #(i - 1, j), #(i, j + 1), #(i, j - 1)] +} + +fn remove_neighbor(actor, point) { + case set_state.check(actor, point) { + True -> { + set_state.drop(actor, point) + list.each(neighbors(point), remove_neighbor(actor, _)) + } + False -> Nil + } +} diff --git a/gleam/aoc2017/src/aoc_2017/day_15.gleam b/gleam/aoc2017/src/aoc_2017/day_15.gleam new file mode 100644 index 0000000..dab5c14 --- /dev/null +++ b/gleam/aoc2017/src/aoc_2017/day_15.gleam @@ -0,0 +1,93 @@ +import gleam/int +import gleam/string + +const sixteen_bits = 0xFFFF + +const generator_a = 16_807 + +const generator_b = 48_271 + +const divisor = 2_147_483_647 + +const max_reps_pt1 = 40_000_000 + +const max_reps_pt2 = 5_000_000 + +pub fn parse(input: String) { + let assert Ok(#(a_str, b_str)) = string.split_once(input, ",") + let assert Ok(a) = int.parse(a_str) + let assert Ok(b) = int.parse(b_str) + + #(a, b) +} + +pub fn pt_1(input: #(Int, Int)) { + let #(a, b) = input + + next_comparison( + a: a, + b: b, + selecting_a_using: next_value, + selecting_b_using: next_value, + initial_matches: 0, + initial_cycle: 0, + up_to: max_reps_pt1, + ) +} + +pub fn pt_2(input: #(Int, Int)) { + let #(a, b) = input + + next_comparison( + a: a, + b: b, + selecting_a_using: next_but_divisible_by(4), + selecting_b_using: next_but_divisible_by(8), + initial_matches: 0, + initial_cycle: 0, + up_to: max_reps_pt2, + ) +} + +fn next_value(current, generator) { + { current * generator } % divisor +} + +fn picky_generator(current, generator, divisible_by) { + let trial = next_value(current, generator) + case trial % divisible_by { + 0 -> trial + _ -> picky_generator(trial, generator, divisible_by) + } +} + +fn next_but_divisible_by(divisible_by) { + fn(c, g) { picky_generator(c, g, divisible_by) } +} + +fn last_16_bits(n: Int) { + int.bitwise_and(n, sixteen_bits) +} + +fn next_comparison( + a a: Int, + b b: Int, + selecting_a_using for_a: fn(Int, Int) -> Int, + selecting_b_using for_b: fn(Int, Int) -> Int, + initial_matches same: Int, + initial_cycle count: Int, + up_to max: Int, +) { + case count == max { + True -> same + False -> { + let next_a = for_a(a, generator_a) + let next_b = for_b(b, generator_b) + let maybe_same = case last_16_bits(next_a) == last_16_bits(next_b) { + True -> same + 1 + False -> same + } + next_comparison(next_a, next_b, for_a, for_b, maybe_same, count + 1, max) + } + } +} diff --git a/gleam/aoc2017/src/aoc_2017/day_16.gleam b/gleam/aoc2017/src/aoc_2017/day_16.gleam new file mode 100644 index 0000000..9a5110e --- /dev/null +++ b/gleam/aoc2017/src/aoc_2017/day_16.gleam @@ -0,0 +1,104 @@ +import gleam/dict.{type Dict} +import gleam/int +import gleam/list +import gleam/string + +pub type DanceMove { + Spin(moving: Int) + Exchange(first: Int, second: Int) + Partner(first: String, second: String) +} + +const initial_lineup = "abcdefghijklmnop" + +const dancer_count = 16 + +const end_state = 1_000_000_000 + +pub fn parse(input: String) { + string.split(input, ",") +} + +fn do_spin(dancers: List(String), moving: Int) { + let #(front, back) = list.split(dancers, dancer_count - moving) + list.append(back, front) +} + +fn do_exchange(dancers: List(String), first: Int, second: Int) { + let indexed = list.index_map(dancers, fn(d, i) { #(i, d) }) + + let assert Ok(first_dancer) = list.key_find(indexed, first) + let assert Ok(second_dancer) = list.key_find(indexed, second) + + indexed + |> list.key_set(first, second_dancer) + |> list.key_set(second, first_dancer) + |> list.map(fn(tup) { tup.1 }) +} + +fn do_partner(dancers: List(String), first: String, second: String) { + use dancer <- list.map(dancers) + case dancer { + d if d == first -> second + d if d == second -> first + d -> d + } +} + +pub fn pt_1(input: List(String)) { + initial_lineup + |> string.to_graphemes() + |> next_move(input) + |> string.concat() +} + +fn next_move(dancers, raw_moves) { + case raw_moves { + [] -> dancers + ["s" <> size, ..rest] -> dancers |> do_spin(int(size)) |> next_move(rest) + ["x" <> swap, ..rest] -> { + let assert Ok(#(first, second)) = string.split_once(swap, "/") + dancers |> do_exchange(int(first), int(second)) |> next_move(rest) + } + ["p" <> swap, ..rest] -> { + let assert Ok(#(first, second)) = string.split_once(swap, "/") + dancers |> do_partner(first, second) |> next_move(rest) + } + _ -> panic as "bad dance move" + } +} + +pub fn pt_2(input: List(String)) { + initial_lineup + |> string.to_graphemes() + |> find_cycle(caching_in: dict.new(), cycle: 0, dancing_to: input) +} + +fn find_cycle( + moving_to dance_position: List(String), + caching_in cache: Dict(String, Int), + cycle cycle: Int, + dancing_to dance_moves: List(String), +) { + let dance_hash = string.concat(dance_position) + case dict.get(cache, dance_hash) { + Ok(c) -> { + let offset = end_state % { cycle - c } - c + let assert [#(final, _)] = + dict.filter(cache, fn(_, v) { v == offset }) |> dict.to_list() + final + } + _err -> + find_cycle( + moving_to: next_move(dance_position, dance_moves), + caching_in: dict.insert(cache, dance_hash, cycle), + cycle: cycle + 1, + dancing_to: dance_moves, + ) + } +} + +fn int(n) { + let assert Ok(n) = int.parse(n) + n +} diff --git a/gleam/aoc2017/src/aoc_2017/day_17.gleam b/gleam/aoc2017/src/aoc_2017/day_17.gleam new file mode 100644 index 0000000..5904cab --- /dev/null +++ b/gleam/aoc2017/src/aoc_2017/day_17.gleam @@ -0,0 +1,55 @@ +import gleam/int +import gleam/list +import glearray + +pub fn parse(input: String) { + let assert Ok(n) = int.parse(input) + n +} + +pub fn pt_1(input: Int) { + let assert [_, result] = + next_spin([0], 0, 1, input) + |> list.drop_while(fn(x) { x != 2017 }) + |> list.take(2) + + result +} + +fn next_spin(list: List(Int), position: Int, cycle: Int, step: Int) { + case cycle { + 2018 -> list + _ -> { + let next_position = { position + step } % cycle + 1 + next_spin( + insert_at(list, next_position, cycle), + next_position, + cycle + 1, + step, + ) + } + } +} + +fn insert_at(xs: List(a), at index: Int, insert new: a) { + let #(left, right) = list.split(xs, index) + list.concat([left, [new], right]) +} + +pub fn pt_2(input: Int) { + next_spin_tracking_zero(0, 0, 1, input) +} + +fn next_spin_tracking_zero(acc: Int, position: Int, cycle: Int, step: Int) { + case cycle { + 50_000_001 -> acc + _ -> { + let next_position = { position + step } % cycle + 1 + let next_acc = case next_position { + 1 -> cycle + _ -> acc + } + next_spin_tracking_zero(next_acc, next_position, cycle + 1, step) + } + } +} diff --git a/gleam/aoc2017/src/aoc_2017/day_2.gleam b/gleam/aoc2017/src/aoc_2017/day_2.gleam new file mode 100644 index 0000000..6a5e85d --- /dev/null +++ b/gleam/aoc2017/src/aoc_2017/day_2.gleam @@ -0,0 +1,47 @@ +import gleam/int +import gleam/list +import gleam/result +import gleam/string + +pub fn parse(input: String) { + use row <- list.map(string.split(input, "\n")) + use val <- list.map(string.split(row, "\t")) + let assert Ok(n) = int.parse(val) + n +} + +pub fn pt_1(input: List(List(Int))) { + use acc, row <- list.fold(input, 0) + acc + max(row) - min(row) +} + +pub fn pt_2(input: List(List(Int))) { + use acc, row <- list.fold(input, 0) + let assert [val] = + row |> list.combination_pairs() |> list.map(test_pair) |> result.values() + acc + val +} + +fn max(xs) { + let assert Ok(result) = list.reduce(xs, int.max) + result +} + +fn min(xs) { + let assert Ok(result) = list.reduce(xs, int.min) + result +} + +fn test_pair(tup) { + case tup { + #(a, b) if a > b -> check_divisibility(a, b) + #(b, a) -> check_divisibility(a, b) + } +} + +fn check_divisibility(a, b) { + case a % b { + 0 -> Ok(a / b) + _ -> Error(Nil) + } +} diff --git a/gleam/aoc2017/src/aoc_2017/day_3.gleam b/gleam/aoc2017/src/aoc_2017/day_3.gleam new file mode 100644 index 0000000..5672e39 --- /dev/null +++ b/gleam/aoc2017/src/aoc_2017/day_3.gleam @@ -0,0 +1,87 @@ +import gleam/dict +import gleam/int +import gleam/list + +type Direction { + Up + Down + Left + Right +} + +type State { + State( + x: Int, + y: Int, + direction: Direction, + branch_length: Int, + remaining: Int, + ) +} + +fn starting_state() -> State { + State(0, 0, Right, 1, 1) +} + +fn update_state(state: State) -> State { + case state { + State(x, y, Right, len, 0) -> State(x, y + 1, Up, len, len - 1) + State(x, y, Up, len, 0) -> State(x - 1, y, Left, len + 1, len) + State(x, y, Left, len, 0) -> State(x, y - 1, Down, len, len - 1) + State(x, y, Down, len, 0) -> State(x + 1, y, Right, len + 1, len) + State(x, y, Right, len, rem) -> State(x + 1, y, Right, len, rem - 1) + State(x, y, Up, len, rem) -> State(x, y + 1, Up, len, rem - 1) + State(x, y, Left, len, rem) -> State(x - 1, y, Left, len, rem - 1) + State(x, y, Down, len, rem) -> State(x, y - 1, Down, len, rem - 1) + } +} + +type Grid = + dict.Dict(#(Int, Int), Int) + +pub fn parse(input: String) -> Int { + let assert Ok(n) = int.parse(input) + n +} + +pub fn pt_1(input: Int) -> Int { + next_step(1, input, starting_state()) +} + +fn next_step(current: Int, target: Int, state: State) -> Int { + case current == target { + True -> int.absolute_value(state.x) + int.absolute_value(state.y) + False -> next_step(current + 1, target, update_state(state)) + } +} + +pub fn pt_2(input: Int) -> Int { + let grid: Grid = dict.from_list([#(#(0, 0), 1)]) + + add_next_cell(input, starting_state(), grid) +} + +fn neighbors(coord: #(Int, Int)) -> List(#(Int, Int)) { + let #(x, y) = coord + + use dx <- list.flat_map(list.range(-1, 1)) + use dy <- list.map(list.range(-1, 1)) + #(x + dx, y + dy) +} + +fn add_next_cell(target: Int, state: State, grid: Grid) -> Int { + let next_cell = update_state(state) + let coords = #(next_cell.x, next_cell.y) + let value = + list.fold(neighbors(coords), 0, fn(acc, coord) { + case dict.get(grid, coord) { + Ok(n) -> acc + n + _err -> acc + } + }) + + case value >= target { + True -> value + False -> add_next_cell(target, next_cell, dict.insert(grid, coords, value)) + } +} diff --git a/gleam/aoc2017/src/aoc_2017/day_4.gleam b/gleam/aoc2017/src/aoc_2017/day_4.gleam new file mode 100644 index 0000000..9bc4f9a --- /dev/null +++ b/gleam/aoc2017/src/aoc_2017/day_4.gleam @@ -0,0 +1,31 @@ +import gleam/list +import gleam/string + +pub fn parse(input: String) -> List(List(String)) { + use row <- list.map(string.split(input, "\n")) + string.split(row, " ") +} + +pub fn pt_1(input: List(List(String))) { + use acc, passwords <- list.fold(input, 0) + case passwords == list.unique(passwords) { + True -> acc + 1 + False -> acc + } +} + +pub fn pt_2(input: List(List(String))) { + use acc, passwords <- list.fold(input, 0) + let sorted_passwords = list.map(passwords, sort_graphemes) + case sorted_passwords == list.unique(sorted_passwords) { + True -> acc + 1 + False -> acc + } +} + +fn sort_graphemes(word: String) -> String { + word + |> string.to_graphemes + |> list.sort(string.compare) + |> string.concat +} diff --git a/gleam/aoc2017/src/aoc_2017/day_5.gleam b/gleam/aoc2017/src/aoc_2017/day_5.gleam new file mode 100644 index 0000000..a0b9b80 --- /dev/null +++ b/gleam/aoc2017/src/aoc_2017/day_5.gleam @@ -0,0 +1,48 @@ +import gary.{type ErlangArray} +import gary/array +import gleam/int +import gleam/list +import gleam/result +import gleam/string + +pub fn parse(input: String) -> ErlangArray(Int) { + input + |> string.split("\n") + |> list.map(int.parse) + |> result.values() + |> array.from_list(default: 0) + |> array.make_fixed() +} + +pub fn pt_1(input: ErlangArray(Int)) { + next_step(input, 0, 0, part: 1) +} + +pub fn pt_2(input: ErlangArray(Int)) { + next_step(input, 0, 0, part: 2) +} + +fn next_step( + instructions: ErlangArray(Int), + pointer: Int, + step: Int, + part part: Int, +) { + case array.get(from: instructions, at: pointer) { + Ok(distance) -> { + let delta = delta(distance, part) + let assert Ok(updated_instructions) = + array.set(instructions, at: pointer, put: distance + delta) + next_step(updated_instructions, pointer + distance, step + 1, part) + } + Error(_) -> step + } +} + +fn delta(d: Int, part: Int) { + case part, d { + 1, _ -> 1 + _, n if n < 3 -> 1 + _, _ -> -1 + } +} diff --git a/gleam/aoc2017/src/aoc_2017/day_6.gleam b/gleam/aoc2017/src/aoc_2017/day_6.gleam new file mode 100644 index 0000000..84222e1 --- /dev/null +++ b/gleam/aoc2017/src/aoc_2017/day_6.gleam @@ -0,0 +1,65 @@ +import gary.{type ErlangArray} +import gary/array +import gleam/bool +import gleam/int +import gleam/list +import gleam/result +import gleam/set.{type Set} +import gleam/string + +pub fn parse(input) -> ErlangArray(Int) { + input + |> string.split("\t") + |> list.map(int.parse) + |> result.values() + |> array.from_list(default: -1) + |> array.make_fixed() +} + +pub fn pt_1(input: ErlangArray(Int)) { + check_cycle(input, set.from_list([input]), 1).1 +} + +pub fn pt_2(input: ErlangArray(Int)) { + let #(target, cycle) = check_cycle(input, set.from_list([input]), 1) + cycle - check_cycle(input, set.from_list([input, target]), 1).1 +} + +fn get_max(array: ErlangArray(Int)) -> #(Int, Int) { + use index, value, max <- array.fold(over: array, from: #(-1, -1)) + case value > max.1 { + True -> #(index, value) + False -> max + } +} + +fn redistribute( + array: ErlangArray(Int), + pointer: Int, + remaining: Int, +) -> ErlangArray(Int) { + use <- bool.guard(remaining == 0, array) + case array.get(from: array, at: pointer) { + Error(_) -> redistribute(array, 0, remaining) + Ok(n) -> { + let assert Ok(updated_array) = + array.set(into: array, at: pointer, put: n + 1) + redistribute(updated_array, pointer + 1, remaining - 1) + } + } +} + +fn check_cycle( + current: ErlangArray(Int), + cache: Set(ErlangArray(Int)), + cycle: Int, +) -> #(ErlangArray(Int), Int) { + let #(index, to_redistribute) = current |> get_max + let assert Ok(zeroed) = array.set(into: current, at: index, put: 0) + let next = redistribute(zeroed, index + 1, to_redistribute) + + case set.contains(cache, next) { + True -> #(next, cycle) + False -> check_cycle(next, set.insert(cache, next), cycle + 1) + } +} diff --git a/gleam/aoc2017/src/aoc_2017/day_7.gleam b/gleam/aoc2017/src/aoc_2017/day_7.gleam new file mode 100644 index 0000000..a289fa5 --- /dev/null +++ b/gleam/aoc2017/src/aoc_2017/day_7.gleam @@ -0,0 +1,108 @@ +import gleam/dict.{type Dict} +import gleam/int +import gleam/list +import gleam/option.{Some} +import gleam/regex.{type Match, Match} +import gleam/set +import gleam/string + +pub type Program { + Program(name: String, weight: Int, supporting: List(String)) +} + +pub fn parse(input: String) { + let assert Ok(re) = regex.from_string("([a-z]+) \\(([0-9]+)\\)(?> -> (.*))?") + + use match <- list.map(string.split(input, "\n")) + case regex.scan(re, match) { + [Match(submatches: [Some(name), Some(weight)], ..)] -> + Program(name, to_int(weight), []) + [Match(submatches: [Some(name), Some(weight), Some(supporting)], ..)] -> + Program(name, to_int(weight), string.split(supporting, ", ")) + _ -> panic as { "couldn't parse" <> match } + } +} + +fn to_int(str: String) -> Int { + let assert Ok(n) = int.parse(str) + n +} + +pub fn pt_1(input: List(Program)) { + let supporters = input |> list.map(fn(p) { p.name }) |> set.from_list() + let supporting = + input |> list.flat_map(fn(p) { p.supporting }) |> set.from_list() + + let assert [base] = set.difference(supporters, supporting) |> set.to_list + base +} + +pub fn pt_2(input: List(Program)) { + let weights = + input |> list.map(fn(p) { #(p.name, p.weight) }) |> dict.from_list + + let supporters = + input + |> list.filter_map(fn(p) { + case list.is_empty(p.supporting) { + True -> Error(Nil) + False -> Ok(#(p.name, p.supporting)) + } + }) + |> dict.from_list + + supporters + |> dict.keys() + |> list.filter_map(fn(name) { + let branch_weights = + name + |> branch_weights(weights, supporters) + |> fn(tup: #(String, List(#(Int, String)))) { tup.1 } + + case + branch_weights + |> list.map(fn(tup: #(Int, String)) { tup.0 }) + |> list.unique + |> list.length + { + 2 -> Ok(#(name, branch_weights)) + _ -> Error(Nil) + } + }) + + // [ + // #("hmgrlpj", [#(2078, "drjmjug"), #(2070, "nigdlq"), #(2070, "omytneg"), ...]), + // #("smaygo", [#(14564, "hmgrlpj"), #(14556, "fbnbt"), #(14556, "nfdvsc")]) + // #("eugwuhl", [#(48292, "smaygo"), #(48284, "pvvbn"), #(48284, "hgizeb"), ...]), + // ] + // + // by inspection, eugwuhl -> smaygo -> hmgrlpj -> drjmjug; changing drjmjug will fix the tower + + let assert Ok(w) = dict.get(weights, "drjmjug") + w - 8 +} + +fn branch_weights( + name: String, + weights: Dict(String, Int), + supporting: Dict(String, List(String)), +) -> #(String, List(#(Int, String))) { + let supported = case dict.get(supporting, name) { + Ok(supported) -> supported + Error(_) -> [] + } + + let supported_weights = + list.map(supported, fn(s) { + let assert Ok(weight) = dict.get(weights, s) + let children_weights = + s + |> branch_weights(weights, supporting) + |> fn(tup: #(String, List(#(Int, String)))) { tup.1 } + |> list.map(fn(tup: #(Int, String)) { tup.0 }) + weight + int.sum(children_weights) + }) + |> list.zip(supported) + + #(name, supported_weights) +} diff --git a/gleam/aoc2017/src/aoc_2017/day_8.gleam b/gleam/aoc2017/src/aoc_2017/day_8.gleam new file mode 100644 index 0000000..2f9d0dc --- /dev/null +++ b/gleam/aoc2017/src/aoc_2017/day_8.gleam @@ -0,0 +1,131 @@ +import gleam/dict +import gleam/int +import gleam/list +import gleam/option.{None, Some} +import gleam/string + +const max_register = "__MAX" + +pub type Instruction { + Instruction(register: String, op: Operation, condition: Condition) +} + +pub type Operation { + Inc(by: Int) + Dec(by: Int) +} + +pub type Condition { + Equal(register: String, value: Int) + NotEqual(register: String, value: Int) + LessThan(register: String, value: Int) + GreaterThan(register: String, value: Int) + LessThanOrEq(register: String, value: Int) + GreaterThanOrEq(register: String, value: Int) +} + +type Registers = + dict.Dict(String, Int) + +pub fn parse(input: String) { + input + |> string.split("\n") + |> list.map(parse_instruction) +} + +fn parse_instruction(str: String) -> Instruction { + case string.split(str, " ") { + [name, op, by, "if", cond_name, cond_type, cond_by] -> + Instruction(name, to_op(op, by), to_cond(cond_name, cond_type, cond_by)) + _ -> panic as { "couldn't parse: " <> str } + } +} + +pub fn pt_1(input: List(Instruction)) { + let registers = dict.new() |> dict.insert(max_register, 0) + + input + |> list.fold(registers, next_instruction) + |> dict.delete(max_register) + |> dict.values() + |> list.reduce(int.max) +} + +fn next_instruction(regs: Registers, inst: Instruction) { + case to_compare_fn(inst.condition)(fetch(inst.condition.register, regs)) { + True -> { + let updated_regs = dict.update(regs, inst.register, to_update_fn(inst.op)) + let assert Ok(max) = updated_regs |> dict.values |> list.reduce(int.max) + dict.insert(updated_regs, max_register, max) + } + False -> regs + } +} + +pub fn pt_2(input: List(Instruction)) { + let registers = dict.new() |> dict.insert(max_register, 0) + + input + |> list.fold(registers, next_instruction) + |> dict.get(max_register) +} + +fn int(str: String) -> Int { + let assert Ok(n) = int.parse(str) + n +} + +fn to_op(raw_op: String, raw_by: String) -> Operation { + case raw_op { + "inc" -> Inc(int(raw_by)) + "dec" -> Dec(int(raw_by)) + _ -> panic as { "bad op: " <> raw_op } + } +} + +fn to_cond(name: String, raw_type: String, raw_by: String) -> Condition { + case raw_type { + "==" -> Equal(name, int(raw_by)) + "!=" -> NotEqual(name, int(raw_by)) + ">" -> GreaterThan(name, int(raw_by)) + "<" -> LessThan(name, int(raw_by)) + ">=" -> GreaterThanOrEq(name, int(raw_by)) + "<=" -> LessThanOrEq(name, int(raw_by)) + _ -> panic as { "bad condition: " <> raw_type } + } +} + +fn to_compare_fn(condition: Condition) -> fn(Int) -> Bool { + case condition { + Equal(value: v, ..) -> fn(a) { a == v } + NotEqual(value: v, ..) -> fn(a) { a != v } + GreaterThan(value: v, ..) -> fn(a) { a > v } + LessThan(value: v, ..) -> fn(a) { a < v } + GreaterThanOrEq(value: v, ..) -> fn(a) { a >= v } + LessThanOrEq(value: v, ..) -> fn(a) { a <= v } + } +} + +fn to_update_fn(op: Operation) { + case op { + Inc(n) -> fn(x) { + case x { + Some(i) -> i + n + None -> n + } + } + Dec(n) -> fn(x) { + case x { + Some(i) -> i - n + None -> -n + } + } + } +} + +fn fetch(name: String, registers: Registers) -> Int { + case dict.get(registers, name) { + Ok(n) -> n + Error(_) -> 0 + } +} diff --git a/gleam/aoc2017/src/aoc_2017/day_9.gleam b/gleam/aoc2017/src/aoc_2017/day_9.gleam new file mode 100644 index 0000000..90eb4b3 --- /dev/null +++ b/gleam/aoc2017/src/aoc_2017/day_9.gleam @@ -0,0 +1,48 @@ +import gleam/list +import gleam/option.{Some} +import gleam/regex +import gleam/string + +pub fn parse(input: String) { + let assert Ok(cancel) = regex.from_string("!.") + + replace(input, with: cancel) +} + +pub fn pt_1(input: String) { + input + |> strip_to_brackets() + |> next_bracket(0, 1) +} + +fn replace(input: String, with regex: regex.Regex) -> String { + input |> regex.split(with: regex) |> string.concat() +} + +fn strip_to_brackets(input: String) -> String { + let assert Ok(garbage) = regex.from_string("<.*?>") + let assert Ok(not_group) = regex.from_string("[^{}]") + + input + |> replace(with: garbage) + |> replace(with: not_group) +} + +fn next_bracket(brackets: String, score: Int, depth: Int) -> Int { + case string.pop_grapheme(brackets) { + Error(Nil) -> score + Ok(#("{", rest)) -> next_bracket(rest, score + depth, depth + 1) + Ok(#("}", rest)) -> next_bracket(rest, score, depth - 1) + _ -> panic as "unrecognized character" + } +} + +pub fn pt_2(input: String) { + let assert Ok(garbage) = regex.from_string("<(.*?)>") + + use acc, match <- list.fold(regex.scan(input, with: garbage), 0) + case match.submatches { + [Some(g)] -> string.length(g) + acc + _ -> acc + } +} diff --git a/gleam/aoc2017/src/helpers/set_state.gleam b/gleam/aoc2017/src/helpers/set_state.gleam new file mode 100644 index 0000000..cbbad81 --- /dev/null +++ b/gleam/aoc2017/src/helpers/set_state.gleam @@ -0,0 +1,55 @@ +import gleam/erlang/process.{type Subject, Normal} +import gleam/option.{None} +import gleam/otp/actor.{type Next, Continue, Stop} +import gleam/set.{type Set} + +const timeout = 1000 + +pub type Message(k) { + Shutdown + Check(key: k, client: Subject(Bool)) + Add(key: k) + Drop(key: k) + Pop(client: Subject(Result(k, Nil))) +} + +fn handle_message(message: Message(k), set: Set(k)) -> Next(Message(k), Set(k)) { + case message { + Shutdown -> Stop(Normal) + Check(key, client) -> { + process.send(client, set.contains(set, key)) + Continue(set, None) + } + Add(key) -> Continue(set.insert(set, key), None) + Drop(key) -> Continue(set.delete(set, key), None) + Pop(client) -> { + case set.to_list(set) { + [next, ..] -> { + process.send(client, Ok(next)) + Continue(set.delete(set, next), None) + } + [] -> { + process.send(client, Error(Nil)) + Stop(Normal) + } + } + } + } +} + +pub fn start_actor(with: Set(a)) { + let assert Ok(actor) = actor.start(with, handle_message) + actor +} + +pub fn pop(actor) { + process.call(actor, Pop, timeout) +} + +pub fn check(actor, value) { + process.call(actor, Check(value, _), timeout) +} + +pub fn drop(actor, value) { + process.send(actor, Drop(value)) +} diff --git a/gleam/aoc2017/test/aoc2017_gleam_test.gleam b/gleam/aoc2017/test/aoc2017_gleam_test.gleam new file mode 100644 index 0000000..3831e7a --- /dev/null +++ b/gleam/aoc2017/test/aoc2017_gleam_test.gleam @@ -0,0 +1,12 @@ +import gleeunit +import gleeunit/should + +pub fn main() { + gleeunit.main() +} + +// gleeunit test functions end in `_test` +pub fn hello_world_test() { + 1 + |> should.equal(1) +} diff --git a/gleam/aoc2019/.github/workflows/test.yml b/gleam/aoc2019/.github/workflows/test.yml new file mode 100644 index 0000000..dd5e246 --- /dev/null +++ b/gleam/aoc2019/.github/workflows/test.yml @@ -0,0 +1,23 @@ +name: test + +on: + push: + branches: + - master + - main + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: erlef/setup-beam@v1 + with: + otp-version: "26.0.2" + gleam-version: "1.2.0" + rebar3-version: "3" + # elixir-version: "1.15.4" + - run: gleam deps download + - run: gleam test + - run: gleam format --check src test diff --git a/gleam/aoc2019/.gitignore b/gleam/aoc2019/.gitignore new file mode 100644 index 0000000..599be4e --- /dev/null +++ b/gleam/aoc2019/.gitignore @@ -0,0 +1,4 @@ +*.beam +*.ez +/build +erl_crash.dump diff --git a/gleam/aoc2019/README.md b/gleam/aoc2019/README.md new file mode 100644 index 0000000..bbf8121 --- /dev/null +++ b/gleam/aoc2019/README.md @@ -0,0 +1,25 @@ +# aoc2019_gleam + +[![Package Version](https://img.shields.io/hexpm/v/aoc2019_gleam)](https://hex.pm/packages/aoc2019_gleam) +[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/aoc2019_gleam/) + +```sh +gleam add aoc2019_gleam +``` +```gleam +import aoc2019_gleam + +pub fn main() { + // TODO: An example of the project in use +} +``` + +Further documentation can be found at . + +## Development + +```sh +gleam run # Run the project +gleam test # Run the tests +gleam shell # Run an Erlang shell +``` diff --git a/gleam/aoc2019/gleam.toml b/gleam/aoc2019/gleam.toml new file mode 100644 index 0000000..c9ede3d --- /dev/null +++ b/gleam/aoc2019/gleam.toml @@ -0,0 +1,21 @@ +name = "aoc2019_gleam" +version = "1.0.0" + +# Fill out these fields if you intend to generate HTML documentation or publish +# your project to the Hex package manager. +# +# description = "" +# licences = ["Apache-2.0"] +# repository = { type = "github", user = "username", repo = "project" } +# links = [{ title = "Website", href = "https://gleam.run" }] +# +# For a full reference of all the available options, you can have a look at +# https://gleam.run/writing-gleam/gleam-toml/. + +[dependencies] +gleam_stdlib = ">= 0.34.0 and < 2.0.0" +gladvent = ">= 0.7.3 and < 1.0.0" +gary = ">= 1.0.1 and < 2.0.0" + +[dev-dependencies] +gleeunit = ">= 1.0.0 and < 2.0.0" diff --git a/gleam/aoc2019/manifest.toml b/gleam/aoc2019/manifest.toml new file mode 100644 index 0000000..12fa60f --- /dev/null +++ b/gleam/aoc2019/manifest.toml @@ -0,0 +1,33 @@ +# This file was generated by Gleam +# You typically do not need to edit this file + +packages = [ + { name = "argv", version = "1.0.2", build_tools = ["gleam"], requirements = [], otp_app = "argv", source = "hex", outer_checksum = "BA1FF0929525DEBA1CE67256E5ADF77A7CDDFE729E3E3F57A5BDCAA031DED09D" }, + { name = "filepath", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "EFB6FF65C98B2A16378ABC3EE2B14124168C0CE5201553DE652E2644DCFDB594" }, + { name = "gary", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gary", source = "hex", outer_checksum = "4C05611EEC74876A1E36309EED22C826B92F22E566579ACBC65C579CD615EC60" }, + { name = "gladvent", version = "0.7.3", build_tools = ["gleam"], requirements = ["argv", "filepath", "gleam_erlang", "gleam_json", "gleam_otp", "gleam_package_interface", "gleam_stdlib", "glint", "parallel_map", "shellout", "simplifile", "snag", "spinner", "tom"], otp_app = "gladvent", source = "hex", outer_checksum = "59D93FD759427BCE8EE9828C0B62A3CD18362225F948C9789D334DCEAD621358" }, + { name = "gleam_community_ansi", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_community_colour", "gleam_stdlib"], otp_app = "gleam_community_ansi", source = "hex", outer_checksum = "FE79E08BF97009729259B6357EC058315B6FBB916FAD1C2FF9355115FEB0D3A4" }, + { name = "gleam_community_colour", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib"], otp_app = "gleam_community_colour", source = "hex", outer_checksum = "795964217EBEDB3DA656F5EB8F67D7AD22872EB95182042D3E7AFEF32D3FD2FE" }, + { name = "gleam_erlang", version = "0.25.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "054D571A7092D2A9727B3E5D183B7507DAB0DA41556EC9133606F09C15497373" }, + { name = "gleam_json", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "thoas"], otp_app = "gleam_json", source = "hex", outer_checksum = "9063D14D25406326C0255BDA0021541E797D8A7A12573D849462CAFED459F6EB" }, + { name = "gleam_otp", version = "0.10.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "0B04FE915ACECE539B317F9652CAADBBC0F000184D586AAAF2D94C100945D72B" }, + { name = "gleam_package_interface", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib"], otp_app = "gleam_package_interface", source = "hex", outer_checksum = "52A721BCA972C8099BB881195D821AAA64B9F2655BECC102165D5A1097731F01" }, + { name = "gleam_stdlib", version = "0.38.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "663CF11861179AF415A625307447775C09404E752FF99A24E2057C835319F1BE" }, + { name = "glearray", version = "0.2.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "glearray", source = "hex", outer_checksum = "9C207E05F38D724F464FA921378DB3ABC2B0A2F5821116D8BC8B2CACC68930D5" }, + { name = "gleeunit", version = "1.1.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "72CDC3D3F719478F26C4E2C5FED3E657AC81EC14A47D2D2DEBB8693CA3220C3B" }, + { name = "glint", version = "1.0.0-rc2", build_tools = ["gleam"], requirements = ["gleam_community_ansi", "gleam_community_colour", "gleam_stdlib", "snag"], otp_app = "glint", source = "hex", outer_checksum = "FD5C47CE237CA67121F3946ADE7C630750BB67F5E8A4717D2DF5B5EE758CCFDB" }, + { name = "parallel_map", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_otp", "gleam_stdlib"], otp_app = "parallel_map", source = "hex", outer_checksum = "653714A9FD63EACD1A9D0A6582A972B0EC109AE275CDDD2E99CFC3DFAFAB9225" }, + { name = "repeatedly", version = "2.1.1", build_tools = ["gleam"], requirements = [], otp_app = "repeatedly", source = "hex", outer_checksum = "38808C3EC382B0CD981336D5879C24ECB37FCB9C1D1BD128F7A80B0F74404D79" }, + { name = "shellout", version = "1.6.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "shellout", source = "hex", outer_checksum = "E2FCD18957F0E9F67E1F497FC9FF57393392F8A9BAEAEA4779541DE7A68DD7E0" }, + { name = "simplifile", version = "1.7.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "1D5DFA3A2F9319EC85825F6ED88B8E449F381B0D55A62F5E61424E748E7DDEB0" }, + { name = "snag", version = "0.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "snag", source = "hex", outer_checksum = "54D32E16E33655346AA3E66CBA7E191DE0A8793D2C05284E3EFB90AD2CE92BCC" }, + { name = "spinner", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_community_ansi", "gleam_erlang", "gleam_stdlib", "glearray", "repeatedly"], otp_app = "spinner", source = "hex", outer_checksum = "200BA3D4A04D468898E63C0D316E23F526E02514BC46454091975CB5BAE41E8F" }, + { name = "thoas", version = "1.2.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "E38697EDFFD6E91BD12CEA41B155115282630075C2A727E7A6B2947F5408B86A" }, + { name = "tom", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "tom", source = "hex", outer_checksum = "A5364613E3DBF77F38EFF81DA9F99324086D029EC2B2D44348762FBE38602311" }, +] + +[requirements] +gary = { version = ">= 1.0.1 and < 2.0.0"} +gladvent = { version = ">= 0.7.3 and < 1.0.0" } +gleam_stdlib = { version = ">= 0.34.0 and < 2.0.0" } +gleeunit = { version = ">= 1.0.0 and < 2.0.0" } diff --git a/gleam/aoc2019/src/aoc2019_gleam.gleam b/gleam/aoc2019/src/aoc2019_gleam.gleam new file mode 100644 index 0000000..c5a7e0a --- /dev/null +++ b/gleam/aoc2019/src/aoc2019_gleam.gleam @@ -0,0 +1,5 @@ +import gladvent + +pub fn main() { + gladvent.main() +} diff --git a/gleam/aoc2019/src/aoc_2019/day_1.gleam b/gleam/aoc2019/src/aoc_2019/day_1.gleam new file mode 100644 index 0000000..8a7fd2d --- /dev/null +++ b/gleam/aoc2019/src/aoc_2019/day_1.gleam @@ -0,0 +1,30 @@ +import gleam/int +import gleam/list +import gleam/result +import gleam/string + +pub fn parse(input: String) -> List(Int) { + input + |> string.split("\n") + |> list.map(int.parse) + |> result.values() +} + +pub fn pt_1(input: List(Int)) { + list.fold(input, 0, fn(total, next) { total + naive_fuel(next) }) +} + +pub fn pt_2(input: List(Int)) { + list.fold(input, 0, fn(total, next) { total + recursive_fuel(next) }) +} + +fn naive_fuel(weight: Int) -> Int { + { weight / 3 } - 2 +} + +fn recursive_fuel(weight: Int) -> Int { + case { weight / 3 } - 2 { + n if n <= 0 -> 0 + n -> n + recursive_fuel(n) + } +} diff --git a/gleam/aoc2019/src/aoc_2019/day_2.gleam b/gleam/aoc2019/src/aoc_2019/day_2.gleam new file mode 100644 index 0000000..8faa0ea --- /dev/null +++ b/gleam/aoc2019/src/aoc_2019/day_2.gleam @@ -0,0 +1,81 @@ +import gary.{type ErlangArray} +import gary/array.{type ArrayError} +import gleam/bool +import gleam/int +import gleam/list +import gleam/result +import gleam/string + +pub fn parse(input: String) -> ErlangArray(Int) { + input + |> string.split(",") + |> list.map(int.parse) + |> result.values() + |> array.from_list(default: -1) + |> array.make_fixed() +} + +pub fn pt_1(input: ErlangArray(Int)) -> Int { + let assert Ok(result) = + input + |> edit_starting_intcodes(12, 2) + |> run_intcode(0) + + result +} + +pub fn pt_2(input: ErlangArray(Int)) -> Int { + let assert [result] = { + use noun <- list.flat_map(list.range(0, 99)) + use verb <- list.filter_map(list.range(0, 99)) + let result = input |> edit_starting_intcodes(noun, verb) |> run_intcode(0) + case result == Ok(19_690_720) { + True -> Ok(100 * noun + verb) + False -> Error(Nil) + } + } + + result +} + +fn run_intcode( + intcode: ErlangArray(Int), + pointer: Int, +) -> Result(Int, ArrayError) { + let assert Ok(op_code) = array.get(intcode, pointer) + let op = get_op(op_code) + + use <- bool.guard(result.is_error(op), array.get(intcode, 0)) + let assert Ok(position_1) = array.get(intcode, pointer + 1) + let assert Ok(position_2) = array.get(intcode, pointer + 2) + let assert Ok(position_3) = array.get(intcode, pointer + 3) + + let assert Ok(value_1) = array.get(intcode, position_1) + let assert Ok(value_2) = array.get(intcode, position_2) + + let assert Ok(f) = op + let new_value = f(value_1, value_2) + let assert Ok(updated_intcode) = array.set(intcode, position_3, new_value) + run_intcode(updated_intcode, pointer + 4) +} + +fn edit_starting_intcodes( + intcodes: ErlangArray(Int), + new_code_1: Int, + new_code_2: Int, +) -> ErlangArray(Int) { + let assert Ok(updated) = + intcodes + |> array.set(at: 1, put: new_code_1) + |> result.try(array.set(into: _, at: 2, put: new_code_2)) + updated +} + +fn get_op(code: Int) -> Result(fn(Int, Int) -> Int, Nil) { + case code { + 1 -> Ok(int.add) + 2 -> Ok(int.multiply) + 99 -> Error(Nil) + _ -> panic as "bad opcode" + } +} diff --git a/gleam/aoc2019/test/aoc2019_gleam_test.gleam b/gleam/aoc2019/test/aoc2019_gleam_test.gleam new file mode 100644 index 0000000..3831e7a --- /dev/null +++ b/gleam/aoc2019/test/aoc2019_gleam_test.gleam @@ -0,0 +1,12 @@ +import gleeunit +import gleeunit/should + +pub fn main() { + gleeunit.main() +} + +// gleeunit test functions end in `_test` +pub fn hello_world_test() { + 1 + |> should.equal(1) +} diff --git a/gleam/aoc2023/.DS_Store b/gleam/aoc2023/.DS_Store new file mode 100644 index 0000000..5172429 Binary files /dev/null and b/gleam/aoc2023/.DS_Store differ diff --git a/gleam/aoc2023/.github/workflows/test.yml b/gleam/aoc2023/.github/workflows/test.yml new file mode 100644 index 0000000..cf2096e --- /dev/null +++ b/gleam/aoc2023/.github/workflows/test.yml @@ -0,0 +1,23 @@ +name: test + +on: + push: + branches: + - master + - main + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: erlef/setup-beam@v1 + with: + otp-version: "26.0.2" + gleam-version: "0.32.4" + rebar3-version: "3" + # elixir-version: "1.15.4" + - run: gleam deps download + - run: gleam test + - run: gleam format --check src test diff --git a/gleam/aoc2023/.gitignore b/gleam/aoc2023/.gitignore new file mode 100644 index 0000000..8248306 --- /dev/null +++ b/gleam/aoc2023/.gitignore @@ -0,0 +1,6 @@ +*.beam +*.ez +build +erl_crash.dump + +aoc.toml \ No newline at end of file diff --git a/gleam/aoc2023/README.md b/gleam/aoc2023/README.md new file mode 100644 index 0000000..3f534e8 --- /dev/null +++ b/gleam/aoc2023/README.md @@ -0,0 +1,28 @@ +# aoc2023 + +[![Package Version](https://img.shields.io/hexpm/v/aoc2023)](https://hex.pm/packages/aoc2023) +[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/aoc2023/) + +## Quick start + +```sh +gleam run # Run the project +gleam test # Run the tests +gleam shell # Run an Erlang shell +``` + +## Installation + +If available on Hex this package can be added to your Gleam project: + +```sh +gleam add aoc2023 +``` + +and its documentation can be found at . + +## Use + +* Set up a solution: `gleam run -m adglent/day ` +* Check against examples: `gleam test -- --modules=day/day_test` +* Get final answer: `gleam run -m day/solve

` \ No newline at end of file diff --git a/gleam/aoc2023/gleam.toml b/gleam/aoc2023/gleam.toml new file mode 100644 index 0000000..8190aef --- /dev/null +++ b/gleam/aoc2023/gleam.toml @@ -0,0 +1,22 @@ +name = "aoc2023" +version = "0.1.0" +gleam = ">= 0.33.0" + +# Fill out these fields if you intend to generate HTML documentation or publish +# your project to the Hex package manager. +# +# description = "" +# licences = ["Apache-2.0"] +# repository = { type = "github", user = "username", repo = "project" } +# links = [{ title = "Website", href = "https://gleam.run" }] + +[dependencies] +gleam_stdlib = "~> 0.33" +simplifile = "~> 1.0" +gleam_erlang = "~> 0.23" +gleam_community_maths = "~> 1.0" +gleam_otp = "~> 0.8" +pqueue = "~> 2.0" + +[dev-dependencies] +adglent = "~> 1.2" diff --git a/gleam/aoc2023/manifest.toml b/gleam/aoc2023/manifest.toml new file mode 100644 index 0000000..416a155 --- /dev/null +++ b/gleam/aoc2023/manifest.toml @@ -0,0 +1,29 @@ +# This file was generated by Gleam +# You typically do not need to edit this file + +packages = [ + { name = "adglent", version = "1.2.0", build_tools = ["gleam"], requirements = ["gap", "gleam_community_ansi", "gleam_erlang", "gleam_http", "gleam_httpc", "gleam_otp", "gleam_stdlib", "glint", "simplifile", "snag", "tom"], otp_app = "adglent", source = "hex", outer_checksum = "A20D35001061F8AD602E3B92FB3AC0E1E4EEC642AD2AAE0ACEAD3A85F37DA7F0" }, + { name = "gap", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_community_ansi", "gleam_stdlib"], otp_app = "gap", source = "hex", outer_checksum = "2EE1B0A17E85CF73A0C1D29DA315A2699117A8F549C8E8D89FA8261BE41EDEB1" }, + { name = "gleam_community_ansi", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_community_colour", "gleam_stdlib"], otp_app = "gleam_community_ansi", source = "hex", outer_checksum = "FE79E08BF97009729259B6357EC058315B6FBB916FAD1C2FF9355115FEB0D3A4" }, + { name = "gleam_community_colour", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_community_colour", source = "hex", outer_checksum = "A49A5E3AE8B637A5ACBA80ECB9B1AFE89FD3D5351FF6410A42B84F666D40D7D5" }, + { name = "gleam_community_maths", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_community_maths", source = "hex", outer_checksum = "E30C61A75051DAF7CFD77C4FBAA04140FDA0B5D831955E7A74521E5576E2780D" }, + { name = "gleam_erlang", version = "0.23.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "C21CFB816C114784E669FFF4BBF433535EEA9960FA2F216209B8691E87156B96" }, + { name = "gleam_http", version = "3.6.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_http", source = "hex", outer_checksum = "8C07DF9DF8CC7F054C650839A51C30A7D3C26482AC241C899C1CEA86B22DBE51" }, + { name = "gleam_httpc", version = "2.1.2", build_tools = ["gleam"], requirements = ["gleam_http", "gleam_stdlib"], otp_app = "gleam_httpc", source = "hex", outer_checksum = "ACD05CA3BAC7780DF5FFAE334621FD199D1B490FAF6ECDFF74316CAA61CE88E6" }, + { name = "gleam_otp", version = "0.8.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "18EF8242A5E54BA92F717C7222F03B3228AEE00D1F286D4C56C3E8C18AA2588E" }, + { name = "gleam_stdlib", version = "0.36.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "C0D14D807FEC6F8A08A7C9EF8DFDE6AE5C10E40E21325B2B29365965D82EB3D4" }, + { name = "glint", version = "0.13.0", build_tools = ["gleam"], requirements = ["gleam_community_ansi", "gleam_community_colour", "gleam_stdlib", "snag"], otp_app = "glint", source = "hex", outer_checksum = "46E56049CD370D61F720D319D0AB970408C9336EEB918F08B5DCB1DCE9845FA3" }, + { name = "pqueue", version = "2.0.7", build_tools = ["rebar3"], requirements = [], otp_app = "pqueue", source = "hex", outer_checksum = "8B0204BB202335890E4E7F9B99A8EC0B84DDB8513EE298EB180EE9B3BCB4C859" }, + { name = "simplifile", version = "1.5.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "EB9AA8E65E5C1E3E0FDCFC81BC363FD433CB122D7D062750FFDF24DE4AC40116" }, + { name = "snag", version = "0.2.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "snag", source = "hex", outer_checksum = "8FD70D8FB3728E08AC425283BB509BB0F012BE1AE218424A597CDE001B0EE589" }, + { name = "tom", version = "0.2.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "tom", source = "hex", outer_checksum = "5C5A9B8586C547F1F39542B1A3BBD9FEE17AFEAB51CE53B32B13D0D46B421249" }, +] + +[requirements] +adglent = { version = "~> 1.2" } +gleam_community_maths = { version = "~> 1.0" } +gleam_erlang = { version = "~> 0.23" } +gleam_otp = { version = "~> 0.8" } +gleam_stdlib = { version = "~> 0.33" } +pqueue = { version = "~> 2.0" } +simplifile = { version = "~> 1.0" } diff --git a/gleam/aoc2023/src/.gitignore b/gleam/aoc2023/src/.gitignore new file mode 100644 index 0000000..bc13a69 --- /dev/null +++ b/gleam/aoc2023/src/.gitignore @@ -0,0 +1 @@ +aoc2023.gleam \ No newline at end of file diff --git a/gleam/aoc2023/src/day1/.gitignore b/gleam/aoc2023/src/day1/.gitignore new file mode 100644 index 0000000..ae40cea --- /dev/null +++ b/gleam/aoc2023/src/day1/.gitignore @@ -0,0 +1 @@ +input.txt \ No newline at end of file diff --git a/gleam/aoc2023/src/day1/solve.gleam b/gleam/aoc2023/src/day1/solve.gleam new file mode 100644 index 0000000..37a19d2 --- /dev/null +++ b/gleam/aoc2023/src/day1/solve.gleam @@ -0,0 +1,57 @@ +import adglent.{First, Second} +import gleam/io +import gleam/list +import gleam/string +import gleam/regex.{type Match, Match} +import gleam/int + +pub fn part1(input: String) { + let assert Ok(re) = regex.from_string("[1-9]") + + input + |> string.split("\n") + |> list.fold(0, fn(acc, s) { + let matches = regex.scan(s, with: re) + + let assert Ok(Match(content: first, ..)) = list.first(matches) + let assert Ok(Match(content: last, ..)) = list.last(matches) + let assert Ok(i) = int.parse(first <> last) + acc + i + }) + |> string.inspect +} + +const substitutions = [ + #("one", "o1e"), + #("two", "t2o"), + #("three", "t3e"), + #("four", "4"), + #("five", "5e"), + #("six", "6"), + #("seven", "7n"), + #("eight", "e8t"), + #("nine", "n9e"), +] + +pub fn part2(input: String) { + list.fold(over: substitutions, from: input, with: fn(acc, sub) { + let #(from, to) = sub + string.replace(in: acc, each: from, with: to) + }) + |> part1 +} + +pub fn main() { + let assert Ok(part) = adglent.get_part() + let assert Ok(input) = adglent.get_input("1") + case part { + First -> + part1(input) + |> adglent.inspect + |> io.println + Second -> + part2(input) + |> adglent.inspect + |> io.println + } +} diff --git a/gleam/aoc2023/src/day10/.gitignore b/gleam/aoc2023/src/day10/.gitignore new file mode 100644 index 0000000..ae40cea --- /dev/null +++ b/gleam/aoc2023/src/day10/.gitignore @@ -0,0 +1 @@ +input.txt \ No newline at end of file diff --git a/gleam/aoc2023/src/day10/solve.gleam b/gleam/aoc2023/src/day10/solve.gleam new file mode 100644 index 0000000..c33634d --- /dev/null +++ b/gleam/aoc2023/src/day10/solve.gleam @@ -0,0 +1,177 @@ +import adglent.{First, Second} +import gleam/bool +import gleam/dict.{type Dict} +import gleam/list +import gleam/int +import gleam/io +import gleam/set.{type Set} +import gleam/string + +type Posn { + Posn(row: Int, col: Int) +} + +fn add_posns(p1: Posn, p2: Posn) -> Posn { + Posn(p1.row + p2.row, p1.col + p2.col) +} + +type PipeGrid = + Dict(Posn, String) + +const north = Posn(-1, 0) + +const south = Posn(1, 0) + +const east = Posn(0, 1) + +const west = Posn(0, -1) + +const initial_directions = [ + #(north, ["|", "7", "F"]), + #(south, ["|", "J", "L"]), + #(east, ["-", "J", "7"]), + #(west, ["-", "F", "L"]), +] + +fn pipe_neighbors(pipe: String) -> List(Posn) { + case pipe { + "|" -> [north, south] + "-" -> [east, west] + "L" -> [north, east] + "F" -> [south, east] + "7" -> [south, west] + "J" -> [north, west] + _ -> panic as "bad pipe" + } +} + +fn make_grid(input: String) -> PipeGrid { + { + use r, row <- list.index_map(string.split(input, "\n")) + use c, col <- list.index_map(string.to_graphemes(row)) + #(Posn(r, c), col) + } + |> list.flatten + |> dict.from_list +} + +fn valid_start_direction(grid: PipeGrid, s: Posn) { + let assert [dir, ..] = { + use d <- list.filter_map(initial_directions) + let #(delta, valids) = d + let neighbor = add_posns(s, delta) + case dict.get(grid, neighbor) { + Ok(pipe) -> + case list.contains(valids, pipe) { + True -> Ok(neighbor) + False -> Error(Nil) + } + Error(_) -> Error(Nil) + } + } + dir +} + +fn to_next_pipe(current: Posn, grid: PipeGrid, acc: List(Posn)) { + let assert [prev, ..] = acc + let assert Ok(pipe) = dict.get(grid, current) + use <- bool.guard(pipe == "S", [current, ..acc]) + let assert [next] = { + pipe + |> pipe_neighbors + |> list.filter_map(fn(p) { + case add_posns(p, current) { + neighbor if neighbor == prev -> Error(Nil) + neighbor -> Ok(neighbor) + } + }) + } + to_next_pipe(next, grid, [current, ..acc]) +} + +pub fn part1(input: String) { + let grid = + input + |> make_grid + + let assert Ok(s) = + grid + |> dict.filter(fn(_, v) { v == "S" }) + |> dict.keys + |> list.first + + grid + |> valid_start_direction(s) + |> to_next_pipe(grid, [s]) + |> list.length + |> fn(i) { { { i - 1 } / 2 } } + |> string.inspect +} + +fn trace_ray(p: Posn, loop: Set(Posn), grid: PipeGrid) -> Bool { + use <- bool.guard(set.contains(loop, p), False) + int.is_odd(count_crossings(p, loop, grid, 0, "")) +} + +fn count_crossings( + p: Posn, + loop: Set(Posn), + grid: PipeGrid, + acc: Int, + corner: String, +) { + let maybe_cell = dict.get(grid, p) + use <- bool.guard(maybe_cell == Error(Nil), acc) + let assert Ok(cell) = maybe_cell + let next = add_posns(p, east) + case set.contains(loop, p) { + False -> count_crossings(next, loop, grid, acc, corner) + True -> + case corner, cell { + _, "|" -> count_crossings(next, loop, grid, acc + 1, corner) + _, "F" | _, "L" -> count_crossings(next, loop, grid, acc, cell) + "F", "J" | "L", "7" -> count_crossings(next, loop, grid, acc + 1, "") + "F", "7" | "L", "J" -> count_crossings(next, loop, grid, acc, "") + _, _ -> count_crossings(next, loop, grid, acc, corner) + } + } +} + +pub fn part2(input: String) { + let grid = + input + |> make_grid + + let assert Ok(s) = + grid + |> dict.filter(fn(_, v) { v == "S" }) + |> dict.keys + |> list.first + + let loop_pipes = + grid + |> valid_start_direction(s) + |> to_next_pipe(grid, [s]) + |> set.from_list + + grid + |> dict.keys + |> list.filter(trace_ray(_, loop_pipes, grid)) + |> list.length() + |> string.inspect +} + +pub fn main() { + let assert Ok(part) = adglent.get_part() + let assert Ok(input) = adglent.get_input("10") + case part { + First -> + part1(input) + |> adglent.inspect + |> io.println + Second -> + part2(input) + |> adglent.inspect + |> io.println + } +} diff --git a/gleam/aoc2023/src/day11/.gitignore b/gleam/aoc2023/src/day11/.gitignore new file mode 100644 index 0000000..ae40cea --- /dev/null +++ b/gleam/aoc2023/src/day11/.gitignore @@ -0,0 +1 @@ +input.txt \ No newline at end of file diff --git a/gleam/aoc2023/src/day11/solve.gleam b/gleam/aoc2023/src/day11/solve.gleam new file mode 100644 index 0000000..35464a1 --- /dev/null +++ b/gleam/aoc2023/src/day11/solve.gleam @@ -0,0 +1,84 @@ +import adglent.{First, Second} +import gleam/io +import gleam/int +import gleam/string +import gleam/list + +type Posn { + Posn(x: Int, y: Int) +} + +fn find_empty(grid: List(List(String))) { + use acc, row, r <- list.index_fold(grid, []) + case list.unique(row) { + ["."] -> [r, ..acc] + _ -> acc + } +} + +fn count_prior_empty_ranks(rank: Int, empty_ranks: List(Int)) -> Int { + empty_ranks + |> list.drop_while(fn(r_empty) { r_empty > rank }) + |> list.length +} + +fn parse_with_expansion(input: String, expansion: Int) -> List(Posn) { + let add = expansion - 1 + let grid = + input + |> string.split("\n") + |> list.map(string.to_graphemes) + + let empty_row_list = find_empty(grid) + let empty_col_list = find_empty(list.transpose(grid)) + + { + use r, row <- list.index_map(grid) + use acc, cell, c <- list.index_fold(over: row, from: []) + + let p = Posn(r, c) + let empty_r = count_prior_empty_ranks(r, empty_row_list) + let empty_c = count_prior_empty_ranks(c, empty_col_list) + case cell { + "#" -> [Posn(p.x + empty_r * add, p.y + empty_c * add), ..acc] + _empty -> acc + } + } + |> list.flatten() +} + +fn all_distances(stars: List(Posn)) -> Int { + use acc, pair <- list.fold(list.combination_pairs(stars), 0) + let #(s1, s2) = pair + acc + int.absolute_value(s1.x - s2.x) + int.absolute_value(s1.y - s2.y) +} + +fn find_distances(input: String, expand_by: Int) -> String { + input + |> parse_with_expansion(expand_by) + |> all_distances + |> string.inspect +} + +pub fn part1(input: String) { + find_distances(input, 2) +} + +pub fn part2(input: String) { + find_distances(input, 1_000_000) +} + +pub fn main() { + let assert Ok(part) = adglent.get_part() + let assert Ok(input) = adglent.get_input("11") + case part { + First -> + part1(input) + |> adglent.inspect + |> io.println + Second -> + part2(input) + |> adglent.inspect + |> io.println + } +} diff --git a/gleam/aoc2023/src/day12/.gitignore b/gleam/aoc2023/src/day12/.gitignore new file mode 100644 index 0000000..ae40cea --- /dev/null +++ b/gleam/aoc2023/src/day12/.gitignore @@ -0,0 +1 @@ +input.txt \ No newline at end of file diff --git a/gleam/aoc2023/src/day12/solve.gleam b/gleam/aoc2023/src/day12/solve.gleam new file mode 100644 index 0000000..893b83c --- /dev/null +++ b/gleam/aoc2023/src/day12/solve.gleam @@ -0,0 +1,90 @@ +import adglent.{First, Second} +import gleam/io +import gleam/string +import gleam/list +import gleam/int +import gleam/result +import utilities/memo.{type Cache} + +type ParserState = + #(String, List(Int), Int, Bool) + +fn parse_folds(input: String, folds: Int) { + let records = string.split(input, "\n") + use record <- list.map(records) + let assert Ok(#(template, sets_str)) = string.split_once(record, " ") + + let template = + template + |> list.repeat(folds) + |> list.intersperse("?") + |> string.concat + let sets = + sets_str + |> string.split(",") + |> list.map(int.parse) + |> result.values + |> list.repeat(folds) + |> list.flatten() + + #(template, sets) +} + +fn do_count( + template: String, + groups: List(Int), + left: Int, + gap: Bool, + cache: Cache(ParserState, Int), +) -> Int { + use <- memo.memoize(cache, #(template, groups, left, gap)) + case template, groups, left, gap { + "", [], 0, _ -> 1 + "?" <> t_rest, [g, ..g_rest], 0, False -> + do_count(t_rest, g_rest, g - 1, g == 1, cache) + + do_count(t_rest, groups, 0, False, cache) + "?" <> t_rest, [], 0, False + | "?" <> t_rest, _, 0, True + | "." <> t_rest, _, 0, _ -> do_count(t_rest, groups, 0, False, cache) + "#" <> t_rest, [g, ..g_rest], 0, False -> + do_count(t_rest, g_rest, g - 1, g == 1, cache) + "?" <> t_rest, gs, l, False | "#" <> t_rest, gs, l, False -> + do_count(t_rest, gs, l - 1, l == 1, cache) + _, _, _, _ -> 0 + } +} + +fn count_solutions(acc: Int, condition: #(String, List(Int))) -> Int { + use cache: Cache(ParserState, Int) <- memo.create() + let #(template, groups) = condition + acc + do_count(template, groups, 0, False, cache) +} + +pub fn part1(input: String) { + input + |> parse_folds(1) + |> list.fold(0, count_solutions) + |> string.inspect +} + +pub fn part2(input: String) { + input + |> parse_folds(5) + |> list.fold(0, count_solutions) + |> string.inspect +} + +pub fn main() { + let assert Ok(part) = adglent.get_part() + let assert Ok(input) = adglent.get_input("12") + case part { + First -> + part1(input) + |> adglent.inspect + |> io.println + Second -> + part2(input) + |> adglent.inspect + |> io.println + } +} diff --git a/gleam/aoc2023/src/day13/.gitignore b/gleam/aoc2023/src/day13/.gitignore new file mode 100644 index 0000000..ae40cea --- /dev/null +++ b/gleam/aoc2023/src/day13/.gitignore @@ -0,0 +1 @@ +input.txt \ No newline at end of file diff --git a/gleam/aoc2023/src/day13/solve.gleam b/gleam/aoc2023/src/day13/solve.gleam new file mode 100644 index 0000000..6f9b9a0 --- /dev/null +++ b/gleam/aoc2023/src/day13/solve.gleam @@ -0,0 +1,87 @@ +import adglent.{First, Second} +import gleam/io +import gleam/list +import gleam/string +import gleam/bool + +type SymmetryType { + Horizontal(Int) + Vertical(Int) +} + +fn is_symmetric(xss: List(List(a)), errs: Int) { + let assert [left, ..right] = xss + do_is_symmetric([left], right, errs) +} + +fn do_is_symmetric( + left: List(List(a)), + right: List(List(a)), + errors: Int, +) -> Result(Int, Nil) { + use <- bool.guard(list.is_empty(right), Error(Nil)) + let assert [h, ..t] = right + let found_errors = + list.zip(list.flatten(left), list.flatten(right)) + |> list.filter(fn(tup) { tup.1 != tup.0 }) + |> list.length + case found_errors == errors { + True -> Ok(list.length(left)) + False -> do_is_symmetric([h, ..left], t, errors) + } +} + +fn get_symmetry_type(xss: List(List(String)), errors: Int) { + case is_symmetric(xss, errors) { + Ok(n) -> Horizontal(n) + _ -> { + let assert Ok(n) = is_symmetric(list.transpose(xss), errors) + Vertical(n) + } + } +} + +fn summarize_notes(symmetries: List(SymmetryType)) { + use acc, note <- list.fold(symmetries, 0) + acc + + case note { + Horizontal(n) -> 100 * n + Vertical(n) -> n + } +} + +fn solve(input: String, errors: Int) { + input + |> string.split("\n\n") + |> list.map(fn(strs) { + strs + |> string.split("\n") + |> list.map(string.to_graphemes) + |> get_symmetry_type(errors) + }) + |> summarize_notes + |> string.inspect +} + +pub fn part1(input: String) { + solve(input, 0) +} + +pub fn part2(input: String) { + solve(input, 1) +} + +pub fn main() { + let assert Ok(part) = adglent.get_part() + let assert Ok(input) = adglent.get_input("13") + case part { + First -> + part1(input) + |> adglent.inspect + |> io.println + Second -> + part2(input) + |> adglent.inspect + |> io.println + } +} diff --git a/gleam/aoc2023/src/day14/.gitignore b/gleam/aoc2023/src/day14/.gitignore new file mode 100644 index 0000000..ae40cea --- /dev/null +++ b/gleam/aoc2023/src/day14/.gitignore @@ -0,0 +1 @@ +input.txt \ No newline at end of file diff --git a/gleam/aoc2023/src/day14/solve.gleam b/gleam/aoc2023/src/day14/solve.gleam new file mode 100644 index 0000000..ecc5361 --- /dev/null +++ b/gleam/aoc2023/src/day14/solve.gleam @@ -0,0 +1,94 @@ +import adglent.{First, Second} +import gleam/dict +import gleam/int +import gleam/io +import gleam/list +import gleam/order +import gleam/string + +fn parse(input) { + input + |> string.split("\n") + |> list.map(string.to_graphemes) + |> list.transpose() +} + +fn roll_boulders(strs: List(String)) { + { + use chunks <- list.map(list.chunk(strs, fn(c) { c == "O" || c == "." })) + list.sort(chunks, order.reverse(string.compare)) + } + |> list.flatten +} + +fn score(matrix) { + use acc, col <- list.fold(matrix, 0) + acc + + { + use col_acc, char, n <- list.index_fold(list.reverse(col), 0) + case char { + "O" -> col_acc + n + 1 + _ -> col_acc + } + } +} + +pub fn part1(input: String) { + input + |> parse + |> list.map(roll_boulders) + |> score() + |> string.inspect +} + +fn rotate(matrix) { + matrix + |> list.map(list.reverse) + |> list.transpose +} + +fn spin(matrix) { + use acc, _ <- list.fold(list.range(1, 4), matrix) + acc + |> list.map(roll_boulders) + |> rotate +} + +fn spin_cycle(matrix) { + let cache = dict.new() + check_if_seen(matrix, cache, 1_000_000_000) +} + +fn check_if_seen(matrix, cache, count) { + case dict.get(cache, matrix) { + Error(Nil) -> + check_if_seen(spin(matrix), dict.insert(cache, matrix, count), count - 1) + Ok(n) -> { + let assert Ok(extra) = int.modulo(count, n - count) + list.fold(list.range(1, extra), matrix, fn(acc, _) { spin(acc) }) + |> score + } + } +} + +pub fn part2(input: String) { + input + |> parse + |> spin_cycle + |> string.inspect +} + +pub fn main() { + let assert Ok(part) = adglent.get_part() + let assert Ok(input) = adglent.get_input("14") + case part { + First -> + part1(input) + |> adglent.inspect + |> io.println + Second -> + part2(input) + |> adglent.inspect + |> io.println + } +} diff --git a/gleam/aoc2023/src/day15/.gitignore b/gleam/aoc2023/src/day15/.gitignore new file mode 100644 index 0000000..ae40cea --- /dev/null +++ b/gleam/aoc2023/src/day15/.gitignore @@ -0,0 +1 @@ +input.txt \ No newline at end of file diff --git a/gleam/aoc2023/src/day15/solve.gleam b/gleam/aoc2023/src/day15/solve.gleam new file mode 100644 index 0000000..a7d250c --- /dev/null +++ b/gleam/aoc2023/src/day15/solve.gleam @@ -0,0 +1,104 @@ +import adglent.{First, Second} +import gleam/io +import gleam/string +import gleam/list +import gleam/int +import gleam/dict.{type Dict} +import gleam/option.{None, Some} + +fn split(input: String) -> List(String) { + input + |> string.split(",") +} + +fn hash_algorithm(str: String) -> Int { + let codepoints = + str + |> string.to_utf_codepoints() + |> list.map(string.utf_codepoint_to_int) + use acc, c <- list.fold(codepoints, 0) + let assert Ok(acc) = int.modulo({ acc + c } * 17, 256) + acc +} + +pub fn part1(input: String) -> String { + input + |> split + |> list.fold(0, fn(acc, str) { acc + hash_algorithm(str) }) + |> string.inspect +} + +type Instruction { + Remove(label: String) + Insert(label: String, focal: Int) +} + +fn read_instruction(str: String) -> Instruction { + case string.split(str, "=") { + [label, focal_str] -> { + let assert Ok(focal) = int.parse(focal_str) + Insert(label, focal) + } + _ -> Remove(string.drop_right(str, 1)) + } +} + +fn parse_instructions(insts: List(String)) -> Dict(Int, List(#(String, Int))) { + use acc, inst <- list.fold(insts, dict.new()) + case read_instruction(inst) { + Remove(label) -> remove_lens(acc, label) + Insert(label, focal) -> insert_lens(acc, label, focal) + } +} + +fn remove_lens(boxes, label) { + use v <- dict.update(boxes, hash_algorithm(label)) + case v { + Some(lenses) -> + case list.key_pop(lenses, label) { + Ok(#(_, updated)) -> updated + Error(Nil) -> lenses + } + None -> [] + } +} + +fn insert_lens(boxes, label, focal) { + use v <- dict.update(boxes, hash_algorithm(label)) + case v { + Some(lenses) -> list.key_set(lenses, label, focal) + None -> [#(label, focal)] + } +} + +fn focusing_power(boxes: Dict(Int, List(#(String, Int)))) -> Int { + use acc, k, v <- dict.fold(boxes, 0) + let box_acc = { + use acc, lens, i <- list.index_fold(v, 0) + acc + lens.1 * { i + 1 } + } + acc + { k + 1 } * box_acc +} + +pub fn part2(input: String) -> String { + input + |> split + |> parse_instructions + |> focusing_power + |> string.inspect +} + +pub fn main() { + let assert Ok(part) = adglent.get_part() + let assert Ok(input) = adglent.get_input("15") + case part { + First -> + part1(input) + |> adglent.inspect + |> io.println + Second -> + part2(input) + |> adglent.inspect + |> io.println + } +} diff --git a/gleam/aoc2023/src/day16/.gitignore b/gleam/aoc2023/src/day16/.gitignore new file mode 100644 index 0000000..ae40cea --- /dev/null +++ b/gleam/aoc2023/src/day16/.gitignore @@ -0,0 +1 @@ +input.txt \ No newline at end of file diff --git a/gleam/aoc2023/src/day16/solve.gleam b/gleam/aoc2023/src/day16/solve.gleam new file mode 100644 index 0000000..65ce36b --- /dev/null +++ b/gleam/aoc2023/src/day16/solve.gleam @@ -0,0 +1,119 @@ +import adglent.{First, Second} +import gleam/bool +import gleam/dict.{type Dict} +import gleam/io +import gleam/list +import gleam/result +import gleam/set.{type Set} +import utilities/array2d.{type Posn, Posn} + +type Direction { + Up + Right + Down + Left +} + +type Light { + Light(posn: Posn, dir: Direction) +} + +fn move(l: Light) -> Light { + let Light(p, dir) = l + case dir { + Up -> Light(..l, posn: Posn(..p, r: p.r - 1)) + Down -> Light(..l, posn: Posn(..p, r: p.r + 1)) + Left -> Light(..l, posn: Posn(..p, c: p.c - 1)) + Right -> Light(..l, posn: Posn(..p, c: p.c + 1)) + } +} + +fn transform(l: Light, cell: Result(String, Nil)) -> List(Light) { + use <- bool.guard(result.is_error(cell), []) + let assert Ok(c) = cell + let Light(p, dir) = l + case dir, c { + // no change + _, "." | Up, "|" | Down, "|" | Left, "-" | Right, "-" -> [l] + // diagonal mirrors + Left, "/" -> [Light(p, Down)] + Down, "/" -> [Light(p, Left)] + Right, "/" -> [Light(p, Up)] + Up, "/" -> [Light(p, Right)] + Left, "\\" -> [Light(p, Up)] + Up, "\\" -> [Light(p, Left)] + Right, "\\" -> [Light(p, Down)] + Down, "\\" -> [Light(p, Right)] + // splitters + Left, "|" | Right, "|" -> [Light(p, Up), Light(p, Down)] + Up, "-" | Down, "-" -> [Light(p, Left), Light(p, Right)] + _, _ -> panic as "unrecognized cell type" + } +} + +fn energize(lights: List(Light), visited: Set(Light), grid: Dict(Posn, String)) { + let next_positions = + lights + |> list.flat_map(fn(l) { + let next = move(l) + transform(next, dict.get(grid, next.posn)) + }) + |> list.filter(fn(l) { !set.contains(visited, l) }) + let all_visited = set.union(set.from_list(next_positions), visited) + case visited == all_visited { + True -> + set.fold(visited, set.new(), fn(acc, l) { set.insert(acc, l.posn) }) + |> set.to_list + |> list.length + False -> energize(next_positions, all_visited, grid) + } +} + +pub fn part1(input: String) { + let grid = array2d.parse_grid(input) + + [Light(Posn(0, -1), Right)] + |> energize(set.new(), grid) +} + +pub fn part2(input: String) { + let grid = array2d.parse_grid(input) + + let Posn(rows, cols) = { + use acc, p <- list.fold(dict.keys(grid), Posn(0, 0)) + case acc.r + acc.c > p.r + p.c { + True -> acc + False -> p + } + } + + let all_starts = + list.concat([ + list.map(list.range(0, rows), fn(r) { Light(Posn(r, -1), Right) }), + list.map(list.range(0, rows), fn(r) { Light(Posn(r, cols + 1), Left) }), + list.map(list.range(0, cols), fn(c) { Light(Posn(-1, c), Down) }), + list.map(list.range(0, cols), fn(c) { Light(Posn(rows + 1, c), Up) }), + ]) + + use acc, p <- list.fold(all_starts, 0) + let energized = energize([p], set.new(), grid) + case acc > energized { + True -> acc + False -> energized + } +} + +pub fn main() { + let assert Ok(part) = adglent.get_part() + let assert Ok(input) = adglent.get_input("16") + case part { + First -> + part1(input) + |> adglent.inspect + |> io.println + Second -> + part2(input) + |> adglent.inspect + |> io.println + } +} diff --git a/gleam/aoc2023/src/day17/.gitignore b/gleam/aoc2023/src/day17/.gitignore new file mode 100644 index 0000000..ae40cea --- /dev/null +++ b/gleam/aoc2023/src/day17/.gitignore @@ -0,0 +1 @@ +input.txt \ No newline at end of file diff --git a/gleam/aoc2023/src/day17/solve.gleam b/gleam/aoc2023/src/day17/solve.gleam new file mode 100644 index 0000000..7a01c4d --- /dev/null +++ b/gleam/aoc2023/src/day17/solve.gleam @@ -0,0 +1,143 @@ +import adglent.{First, Second} +import gleam/bool +import gleam/dict.{type Dict} +import gleam/io +import gleam/list +import gleam/result +import gleam/string +import gleam/set.{type Set} +import utilities/array2d.{type Posn, Posn} +import utilities/prioqueue.{type PriorityQueue} + +type State { + State(posn: Posn, heatloss: Int, previous: Posn, history: List(Posn)) +} + +const deltas = [Posn(-1, 0), Posn(1, 0), Posn(0, -1), Posn(0, 1)] + +fn make_key(s: State) { + #(s.posn, same_dir(s)) +} + +fn same_dir(s: State) { + case s.history { + [] -> [] + [first, ..] as deltas -> + list.take_while(deltas, fn(d) { d == first }) + |> list.take(10) + } +} + +fn is_goal(s: State, min_run: Int, goal: Posn) { + goal == s.posn && list.length(same_dir(s)) >= min_run +} + +fn find_good_neighbors(max: Int, min: Int, s: State, grid) { + deltas + |> list.filter(eliminate_bad_neighbors(_, s, max, min, grid)) + |> list.map(make_state(_, s, grid)) +} + +fn eliminate_bad_neighbors(d: Posn, s: State, max, min, grid) { + let neighbor = array2d.add_posns(d, s.posn) + + use <- bool.guard( + neighbor == s.previous || !dict.has_key(grid, neighbor), + False, + ) + case same_dir(s), list.length(same_dir(s)) { + [prev, ..], l if l == max -> d != prev + _, 0 -> True + [prev, ..], l if l < min -> d == prev + _, _ -> True + } +} + +fn make_state(d: Posn, s: State, grid) { + let neighbor = array2d.add_posns(d, s.posn) + let assert Ok(heat_lost) = dict.get(grid, neighbor) + State( + posn: neighbor, + heatloss: s.heatloss + heat_lost, + previous: s.posn, + history: [d, ..s.history], + ) +} + +fn find_path( + grid: Dict(Posn, Int), + queue: PriorityQueue(State), + seen: Set(#(Posn, List(Posn))), + get_neighbors: fn(State) -> List(State), + is_goal: fn(State) -> Bool, +) { + let assert Ok(#(state, rest)) = prioqueue.pop(queue) + let key = + make_key( + state + |> io.debug, + ) + case set.contains(seen, key) { + True -> find_path(grid, rest, seen, get_neighbors, is_goal) + False -> { + let now_seen = set.insert(seen, key) + let neighbors = get_neighbors(state) + case list.find(neighbors, is_goal) { + Ok(final) -> final.heatloss + _err -> { + let now_queue = + list.fold(neighbors, rest, fn(acc, n) { + prioqueue.insert(acc, n, n.heatloss) + }) + find_path(grid, now_queue, now_seen, get_neighbors, is_goal) + } + } + } + } +} + +pub fn part1(input: String) { + let raw_grid = + input + |> array2d.to_list_of_lists + + let grid = array2d.to_2d_intarray(raw_grid) + + let rmax = list.length(raw_grid) + let assert Ok(cmax) = + raw_grid + |> list.first + |> result.map(list.length) + + let start = State(Posn(0, 0), 0, Posn(0, 0), []) + let goal = Posn(rmax, cmax) + + find_path( + grid, + prioqueue.insert(prioqueue.new(), start, 0), + set.new(), + find_good_neighbors(0, 3, _, grid), + is_goal(_, 1, goal), + ) + |> string.inspect +} + +pub fn part2(input: String) { + input + |> string.inspect +} + +pub fn main() { + let assert Ok(part) = adglent.get_part() + let assert Ok(input) = adglent.get_input("17") + case part { + First -> + part1(input) + |> adglent.inspect + |> io.println + Second -> + part2(input) + |> adglent.inspect + |> io.println + } +} diff --git a/gleam/aoc2023/src/day18/.gitignore b/gleam/aoc2023/src/day18/.gitignore new file mode 100644 index 0000000..ae40cea --- /dev/null +++ b/gleam/aoc2023/src/day18/.gitignore @@ -0,0 +1 @@ +input.txt \ No newline at end of file diff --git a/gleam/aoc2023/src/day18/solve.gleam b/gleam/aoc2023/src/day18/solve.gleam new file mode 100644 index 0000000..2c000f9 --- /dev/null +++ b/gleam/aoc2023/src/day18/solve.gleam @@ -0,0 +1,113 @@ +import adglent.{First, Second} +import gleam/io +import gleam/int +import gleam/list +import gleam/option.{Some} +import gleam/regex.{type Match, Match} +import gleam/string + +type Coord { + Coord(x: Int, y: Int) +} + +type Direction { + Up + Right + Down + Left +} + +type Dig { + Dig(dir: Direction, dist: Int) +} + +fn to_direction(c: String) { + case c { + "R" | "0" -> Right + "D" | "1" -> Down + "L" | "2" -> Left + "U" | "3" -> Up + _ -> panic + } +} + +fn parse_front(line: String) { + let assert Ok(re) = regex.from_string("(.) (.*) \\(.*\\)") + let assert [Match(submatches: [Some(dir), Some(dist)], ..)] = + regex.scan(with: re, content: line) + let assert Ok(n) = int.parse(dist) + Dig(to_direction(dir), n) +} + +fn parse_hex(line: String) { + let assert Ok(re) = regex.from_string("\\(#(.....)(.)\\)") + let assert [Match(submatches: [Some(dist), Some(dir)], ..)] = + regex.scan(with: re, content: line) + let assert Ok(n) = int.base_parse(dist, 16) + Dig(to_direction(dir), n) +} + +fn go(current: Coord, dig: Dig) { + case dig { + Dig(Up, n) -> Coord(current.x, current.y + n) + Dig(Right, n) -> Coord(current.x + n, current.y) + Dig(Down, n) -> Coord(current.x, current.y - n) + Dig(Left, n) -> Coord(current.x - n, current.y) + } +} + +fn double_triangle(c1: Coord, c2: Coord) { + { c1.x * c2.y } - { c2.x * c1.y } +} + +fn start_dig(digs: List(Dig)) { + do_next_dig(digs, Coord(0, 0), 0, 0) +} + +fn do_next_dig( + digs: List(Dig), + current: Coord, + area: Int, + perimeter: Int, +) -> Int { + case digs { + [] -> int.absolute_value(area) / 2 + { perimeter / 2 } + 1 + [dig, ..rest] -> { + let next = go(current, dig) + let area = area + double_triangle(current, next) + let perimeter = perimeter + dig.dist + do_next_dig(rest, next, area, perimeter) + } + } +} + +fn solve_with(input, f) { + input + |> string.split("\n") + |> list.map(f) + |> start_dig + |> string.inspect +} + +pub fn part1(input: String) { + solve_with(input, parse_front) +} + +pub fn part2(input: String) { + solve_with(input, parse_hex) +} + +pub fn main() { + let assert Ok(part) = adglent.get_part() + let assert Ok(input) = adglent.get_input("18") + case part { + First -> + part1(input) + |> adglent.inspect + |> io.println + Second -> + part2(input) + |> adglent.inspect + |> io.println + } +} diff --git a/gleam/aoc2023/src/day19/.gitignore b/gleam/aoc2023/src/day19/.gitignore new file mode 100644 index 0000000..ae40cea --- /dev/null +++ b/gleam/aoc2023/src/day19/.gitignore @@ -0,0 +1 @@ +input.txt \ No newline at end of file diff --git a/gleam/aoc2023/src/day19/solve.gleam b/gleam/aoc2023/src/day19/solve.gleam new file mode 100644 index 0000000..186e783 --- /dev/null +++ b/gleam/aoc2023/src/day19/solve.gleam @@ -0,0 +1,255 @@ +import adglent.{First, Second} +import gleam/io +import gleam/string +import gleam/dict.{type Dict} +import gleam/order.{type Order, Gt, Lt} +import gleam/regex.{type Match, Match} +import gleam/list +import gleam/option.{Some} +import gleam/int + +type Rating { + XtremelyCool + Musical + Aerodynamic + Shiny +} + +type Part { + Part(x: Int, m: Int, a: Int, s: Int) +} + +type Action { + Accept + Reject + SendTo(String) +} + +type Rule { + If(rating: Rating, comparison: Order, threshold: Int, do: Action) + Just(do: Action) +} + +type Workflow = + Dict(String, List(Rule)) + +type Interval { + Interval(min: Int, max: Int) +} + +type PartRange { + PartRange(x: Interval, m: Interval, a: Interval, s: Interval) +} + +fn parse_workflow(input: String) -> Workflow { + let assert Ok(re) = regex.from_string("(.*){(.*)}") + + use acc, line <- list.fold(string.split(input, "\n"), dict.new()) + let assert [Match(submatches: [Some(name), Some(all_rules)], ..)] = + regex.scan(re, line) + let rules = + string.split(all_rules, ",") + |> parse_rules + dict.insert(acc, name, rules) +} + +fn parse_rules(rules: List(String)) -> List(Rule) { + let assert Ok(re_rule) = regex.from_string("(.*)(>|<)(.*):(.*)") + use rule <- list.map(rules) + case regex.scan(re_rule, rule) { + [Match(submatches: [Some(r), Some(c), Some(t), Some(i)], ..)] -> + If(to_rating(r), to_comp(c), to_val(t), to_instruction(i)) + _nomatch -> Just(to_instruction(rule)) + } +} + +fn to_instruction(rule: String) { + case rule { + "A" -> Accept + "R" -> Reject + name -> SendTo(name) + } +} + +fn to_rating(rating: String) { + case rating { + "x" -> XtremelyCool + "m" -> Musical + "a" -> Aerodynamic + _s -> Shiny + } +} + +fn get_rating(part: Part, rating: Rating) -> Int { + case rating { + XtremelyCool -> part.x + Musical -> part.m + Aerodynamic -> part.a + Shiny -> part.s + } +} + +fn to_comp(comp: String) { + case comp { + "<" -> Lt + _gt -> Gt + } +} + +fn to_val(val: String) { + let assert Ok(n) = int.parse(val) + n +} + +fn parse_parts(input: String) -> List(Part) { + let assert Ok(re) = regex.from_string("{x=(.*),m=(.*),a=(.*),s=(.*)}") + + use part <- list.map(string.split(input, "\n")) + let assert [Match(submatches: [Some(x), Some(m), Some(a), Some(s)], ..)] = + regex.scan(re, part) + Part(to_val(x), to_val(m), to_val(a), to_val(s)) +} + +fn start_evaluating_workflow(part: Part, workflow: Workflow) -> Int { + evaluate_workflow(part, "in", workflow) +} + +fn evaluate_workflow(part: Part, name: String, workflow: Workflow) -> Int { + let assert Ok(rules) = dict.get(workflow, name) + case evaluate_rules(part, rules) { + Accept -> part.x + part.m + part.a + part.s + Reject -> 0 + SendTo(name) -> evaluate_workflow(part, name, workflow) + } +} + +fn evaluate_rules(part: Part, rules: List(Rule)) -> Action { + case rules { + [] -> panic + [Just(do), ..] -> do + [If(rating, comparison, threshold, do), ..rest] -> + case int.compare(get_rating(part, rating), threshold) == comparison { + True -> do + False -> evaluate_rules(part, rest) + } + } +} + +pub fn part1(input: String) { + let assert Ok(#(workflows_str, parts_str)) = string.split_once(input, "\n\n") + + let workflows = parse_workflow(workflows_str) + let parts = parse_parts(parts_str) + + list.map(parts, start_evaluating_workflow(_, workflows)) + |> int.sum + |> string.inspect +} + +fn size(interval: Interval) { + interval.max - interval.min + 1 +} + +fn all_in_range(pr: PartRange) { + size(pr.x) * size(pr.m) * size(pr.a) * size(pr.s) +} + +fn get_partrange(pr: PartRange, rating: Rating) -> Interval { + case rating { + XtremelyCool -> pr.x + Musical -> pr.m + Aerodynamic -> pr.a + Shiny -> pr.s + } +} + +fn update_partrange(pr: PartRange, rating: Rating, i: Interval) -> PartRange { + case rating { + XtremelyCool -> PartRange(..pr, x: i) + Musical -> PartRange(..pr, m: i) + Aerodynamic -> PartRange(..pr, a: i) + Shiny -> PartRange(..pr, s: i) + } +} + +pub fn part2(input: String) { + let assert Ok(#(workflows_str, _)) = string.split_once(input, "\n\n") + + let workflow = parse_workflow(workflows_str) + let start = Interval(1, 4000) + + PartRange(start, start, start, start) + |> evaluate_workflow_on_range("in", workflow) + |> string.inspect +} + +fn evaluate_workflow_on_range( + pr: PartRange, + name: String, + workflow: Workflow, +) -> Int { + let assert Ok(rules) = dict.get(workflow, name) + evaluate_rules_on_range(pr, rules, workflow) +} + +fn evaluate_rules_on_range( + pr: PartRange, + rules: List(Rule), + workflow: Workflow, +) -> Int { + case rules { + [Just(Accept), ..] -> all_in_range(pr) + [Just(Reject), ..] -> 0 + [Just(SendTo(name)), ..] -> evaluate_workflow_on_range(pr, name, workflow) + [If(rating, comparison, t, action), ..rest] -> { + let mod_i = get_partrange(pr, rating) + case comparison { + Lt -> + split_range( + keep: update_partrange(pr, rating, Interval(mod_i.min, t - 1)), + and_do: action, + pass: update_partrange(pr, rating, Interval(t, mod_i.max)), + and_eval: rest, + with: workflow, + ) + _gt -> + split_range( + keep: update_partrange(pr, rating, Interval(t + 1, mod_i.max)), + and_do: action, + pass: update_partrange(pr, rating, Interval(mod_i.min, t)), + and_eval: rest, + with: workflow, + ) + } + } + [] -> panic + } +} + +fn split_range( + keep keep: PartRange, + and_do action: Action, + pass pass: PartRange, + and_eval rest: List(Rule), + with workflow: Workflow, +) -> Int { + int.add( + evaluate_rules_on_range(keep, [Just(action)], workflow), + evaluate_rules_on_range(pass, rest, workflow), + ) +} + +pub fn main() { + let assert Ok(part) = adglent.get_part() + let assert Ok(input) = adglent.get_input("19") + case part { + First -> + part1(input) + |> adglent.inspect + |> io.println + Second -> + part2(input) + |> adglent.inspect + |> io.println + } +} diff --git a/gleam/aoc2023/src/day2/.gitignore b/gleam/aoc2023/src/day2/.gitignore new file mode 100644 index 0000000..ae40cea --- /dev/null +++ b/gleam/aoc2023/src/day2/.gitignore @@ -0,0 +1 @@ +input.txt \ No newline at end of file diff --git a/gleam/aoc2023/src/day2/solve.gleam b/gleam/aoc2023/src/day2/solve.gleam new file mode 100644 index 0000000..608955f --- /dev/null +++ b/gleam/aoc2023/src/day2/solve.gleam @@ -0,0 +1,66 @@ +import adglent.{First, Second} +import gleam/io +import gleam/int +import gleam/string +import gleam/list + +pub type Game { + Game(red: Int, blue: Int, green: Int) +} + +fn parse(input: String) -> List(List(Game)) { + use line <- list.map(string.split(input, "\n")) + let assert [_, rounds] = string.split(line, on: ": ") + use match <- list.map(string.split(rounds, on: "; ")) + use acc, draw <- list.fold( + over: string.split(match, on: ", "), + from: Game(0, 0, 0), + ) + let assert Ok(#(n_str, color)) = string.split_once(draw, " ") + let assert Ok(n) = int.parse(n_str) + case color { + "red" -> Game(..acc, red: n) + "blue" -> Game(..acc, blue: n) + "green" -> Game(..acc, green: n) + _ -> panic as "unrecognized color" + } +} + +pub fn part1(input: String) { + use acc, game, i <- list.index_fold(parse(input), 0) + case list.any(game, fn(m) { m.red > 12 || m.green > 13 || m.blue > 14 }) { + False -> acc + i + 1 + True -> acc + } +} + +pub fn part2(input: String) { + { + use game <- list.map(parse(input)) + use acc, match <- list.fold(game, Game(0, 0, 0)) + let Game(red: red, green: green, blue: blue) = match + Game( + red: int.max(red, acc.red), + blue: int.max(blue, acc.blue), + green: int.max(green, acc.green), + ) + } + |> list.fold(from: 0, with: fn(acc, g: Game) { + acc + g.red * g.blue * g.green + }) +} + +pub fn main() { + let assert Ok(part) = adglent.get_part() + let assert Ok(input) = adglent.get_input("2") + case part { + First -> + part1(input) + |> adglent.inspect + |> io.println + Second -> + part2(input) + |> adglent.inspect + |> io.println + } +} diff --git a/gleam/aoc2023/src/day20/.gitignore b/gleam/aoc2023/src/day20/.gitignore new file mode 100644 index 0000000..ae40cea --- /dev/null +++ b/gleam/aoc2023/src/day20/.gitignore @@ -0,0 +1 @@ +input.txt \ No newline at end of file diff --git a/gleam/aoc2023/src/day20/solve.gleam b/gleam/aoc2023/src/day20/solve.gleam new file mode 100644 index 0000000..9192dac --- /dev/null +++ b/gleam/aoc2023/src/day20/solve.gleam @@ -0,0 +1,251 @@ +import adglent.{First, Second} +import gleam/bool +import gleam/dict.{type Dict} +import gleam/io +import gleam/iterator.{type Iterator, type Step, Next} +import gleam/list +import gleam/queue.{type Queue} +import gleam/set +import gleam/string + +type Node { + Broadcaster(children: List(String)) + Flipflop(children: List(String), state: Power) + Conjunction(children: List(String), state: Dict(String, TonePitch)) + Ground +} + +type Tone { + Tone(from: String, to: String, pitch: TonePitch) +} + +type Power { + On + Off +} + +type TonePitch { + Low + High +} + +type State { + State( + nodes: Dict(String, Node), + low: Int, + high: Int, + cycle: Int, + sentry_nodes: Dict(String, Int), + ) +} + +fn flip_power(p: Power) -> Power { + case p { + On -> Off + Off -> On + } +} + +fn flip_flop_pitch(p: Power) -> TonePitch { + case p { + Off -> High + On -> Low + } +} + +fn combinator_pitch(state) { + case list.unique(dict.values(state)) { + [High] -> Low + _ -> High + } +} + +fn get_children(node) { + case node { + Flipflop(children: cs, ..) -> cs + Conjunction(children: cs, ..) -> cs + Broadcaster(children: cs) -> cs + Ground -> [] + } +} + +fn parse_node(input: String) -> #(String, Node) { + let assert [full_name, children_str] = string.split(input, on: " -> ") + let children = string.split(children_str, on: ", ") + + case full_name { + "%" <> name -> #(name, Flipflop(children: children, state: Off)) + "&" <> name -> #(name, Conjunction(children: children, state: dict.new())) + "broadcaster" -> #("broadcaster", Broadcaster(children: children)) + name -> #(name, Ground) + } +} + +fn to_initial_state(nodes: List(#(String, Node))) -> Dict(String, Node) { + let node_dict = dict.from_list(nodes) + let node_names = dict.keys(node_dict) + + let node_dict = + node_dict + |> dict.values + |> list.map(get_children) + |> list.concat + |> set.from_list + |> set.drop(dict.keys(node_dict)) + |> set.to_list + |> list.fold(node_dict, fn(acc, n) { dict.insert(acc, n, Ground) }) + + use name, node <- dict.map_values(node_dict) + case node { + Conjunction(state: _, children: chs) -> + node_names + |> list.filter(fn(n) { + let assert Ok(node) = dict.get(node_dict, n) + list.contains(get_children(node), any: name) + }) + |> list.map(fn(n) { #(n, Low) }) + |> dict.from_list() + |> fn(dict) { Conjunction(state: dict, children: chs) } + other -> other + } +} + +fn add_to_queue(from, children, pitch, queue) { + use acc, c <- list.fold(children, queue) + queue.push_back(acc, Tone(from: from, to: c, pitch: pitch)) +} + +fn add_tones(state: State, nodes, pitch, n) { + case pitch { + Low -> + State(..state, nodes: nodes, low: state.low + n, cycle: state.cycle + 1) + High -> + State(..state, nodes: nodes, high: state.high + n, cycle: state.cycle + 1) + } +} + +fn press_button_once(initial: State, queue: Queue(Tone)) { + let State(nodes: nodes, ..) = initial + + use <- bool.guard(queue.is_empty(queue), initial) + let assert Ok(#(Tone(from_name, to_name, pitch), rest)) = + queue.pop_front(queue) + + let assert Ok(to_node) = dict.get(nodes, to_name) + case to_node { + Broadcaster(children) -> { + let new_state = + add_tones(initial, nodes, pitch, list.length(children) + 1) + + let new_queue = add_to_queue(to_name, children, pitch, rest) + press_button_once(new_state, new_queue) + } + + Conjunction(state: state, children: children) -> { + let new_state = + state + |> dict.insert(from_name, pitch) + + let updated_nodes = + Conjunction(state: new_state, children: children) + |> dict.insert(nodes, to_name, _) + + let pitch_out = combinator_pitch(new_state) + + let new_state = + add_tones(initial, updated_nodes, pitch_out, list.length(children)) + |> check_for_interesting_node(from_name, pitch_out) + + add_to_queue(to_name, children, pitch_out, rest) + |> press_button_once(new_state, _) + } + + Flipflop(..) if pitch == High -> + press_button_once(State(..initial, cycle: initial.cycle + 1), rest) + + Flipflop(state: state, children: children) -> { + let updated_nodes = + Flipflop(state: flip_power(state), children: children) + |> dict.insert(nodes, to_name, _) + + let pitch_out = flip_flop_pitch(state) + let new_state = + add_tones(initial, updated_nodes, pitch_out, list.length(children)) + + add_to_queue(to_name, children, flip_flop_pitch(state), rest) + |> press_button_once(new_state, _) + } + + Ground(..) -> + press_button_once(State(..initial, cycle: initial.cycle + 1), rest) + } +} + +pub fn part1(input: String) { + let initial_state = + input + |> string.split(on: "\n") + |> list.map(parse_node) + |> to_initial_state() + + iterator.iterate( + from: State(initial_state, 0, 0, 1, dict.new()), + with: press_button_once(_, queue.from_list([ + Tone("button", "broadcaster", Low), + ])), + ) + |> iterator.at(1000) + |> fn(s) { + let assert Ok(State(high: high, low: low, ..)) = s + high * low + } + |> string.inspect +} + +fn check_for_interesting_node(state, name, pitch_out) { + case name, pitch_out { + "rk", High | "cd", High | "zf", High | "qx", High -> + State( + ..state, + sentry_nodes: dict.insert(state.sentry_nodes, name, state.cycle), + ) + _, _ -> state + } +} + +pub fn part2(input: String) { + let initial_state = + input + |> string.split(on: "\n") + |> list.map(parse_node) + |> to_initial_state() + + iterator.iterate( + from: State(initial_state, 0, 0, 1, dict.new()), + with: press_button_once(_, queue.from_list([ + Tone("button", "broadcaster", Low), + ])), + ) + |> iterator.drop_while(fn(s) { dict.size(s.sentry_nodes) < 4 }) + |> iterator.step + |> fn(s: Step(State, Iterator(State))) { + let assert Next(goal, _rest) = s + goal.sentry_nodes + } + |> string.inspect +} + +pub fn main() { + let assert Ok(part) = adglent.get_part() + let assert Ok(input) = adglent.get_input("20") + case part { + First -> + part1(input) + |> adglent.inspect + |> io.println + Second -> + part2(input) + |> adglent.inspect + |> io.println + } +} diff --git a/gleam/aoc2023/src/day21/.gitignore b/gleam/aoc2023/src/day21/.gitignore new file mode 100644 index 0000000..ae40cea --- /dev/null +++ b/gleam/aoc2023/src/day21/.gitignore @@ -0,0 +1 @@ +input.txt \ No newline at end of file diff --git a/gleam/aoc2023/src/day21/solve.gleam b/gleam/aoc2023/src/day21/solve.gleam new file mode 100644 index 0000000..4d5c246 --- /dev/null +++ b/gleam/aoc2023/src/day21/solve.gleam @@ -0,0 +1,25 @@ +import adglent.{First, Second} +import gleam/io + +pub fn part1(input: String) { + todo as "Implement solution to part 1" +} + +pub fn part2(input: String) { + todo as "Implement solution to part 2" +} + +pub fn main() { + let assert Ok(part) = adglent.get_part() + let assert Ok(input) = adglent.get_input("21") + case part { + First -> + part1(input) + |> adglent.inspect + |> io.println + Second -> + part2(input) + |> adglent.inspect + |> io.println + } +} diff --git a/gleam/aoc2023/src/day22/.gitignore b/gleam/aoc2023/src/day22/.gitignore new file mode 100644 index 0000000..ae40cea --- /dev/null +++ b/gleam/aoc2023/src/day22/.gitignore @@ -0,0 +1 @@ +input.txt \ No newline at end of file diff --git a/gleam/aoc2023/src/day22/solve.gleam b/gleam/aoc2023/src/day22/solve.gleam new file mode 100644 index 0000000..7bf2fb4 --- /dev/null +++ b/gleam/aoc2023/src/day22/solve.gleam @@ -0,0 +1,199 @@ +import adglent.{First, Second} +import gleam/bool +import gleam/dict.{type Dict} +import gleam/int +import gleam/io +import gleam/list +import gleam/option.{None, Some} +import gleam/regex +import gleam/result +import gleam/set.{type Set} +import gleam/string + +type Point { + Point(x: Int, y: Int, z: Int) +} + +fn down_one(p: Point) -> Point { + Point(..p, z: p.z - 1) +} + +type Block { + Block(index: Int, from: Point, to: Point) +} + +fn compare_blocks(b1: Block, b2: Block) { + int.compare(b1.to.z, b2.to.z) +} + +type Space = + Dict(Point, Block) + +type AllBlocks = + Dict(Block, List(Point)) + +type BlockTree = + Dict(Int, Set(Int)) + +fn parse_block(index: Int, input: String) -> Block { + let assert Ok(re) = regex.from_string("(.*),(.*),(.*)~(.*),(.*),(.*)") + + let assert [scan] = regex.scan(with: re, content: input) + + let assert [x1, y1, z1, x2, y2, z2] = + scan.submatches + |> option.all + |> option.unwrap([]) + |> list.map(int.parse) + |> result.values + Block(index: index, from: Point(x1, y1, z1), to: Point(x2, y2, z2)) +} + +fn cross_section_at_level(b: Block, z: Int) -> List(Point) { + use x <- list.flat_map(list.range(b.from.x, b.to.x)) + use y <- list.map(list.range(b.from.y, b.to.y)) + Point(x, y, z) +} + +fn place_block(space: Space, b: Block, z: Int) -> Space { + let now_occupied = { + use x <- list.flat_map(list.range(b.from.x, b.to.x)) + use y <- list.flat_map(list.range(b.from.y, b.to.y)) + use z <- list.map(list.range(z, z + b.to.z - b.from.z)) + #(Point(x, y, z), b) + } + + dict.merge(space, dict.from_list(now_occupied)) +} + +fn find_lowest_level(space: Space, b: Block) -> Space { + do_find_lowest(space, b, b.from.z) +} + +fn do_find_lowest(space: Space, b: Block, z: Int) -> Space { + let is_intersecting = + list.any(cross_section_at_level(b, z), dict.has_key(space, _)) + + case z, is_intersecting { + 0, _ -> place_block(space, b, 1) + _, True -> place_block(space, b, z + 1) + _, False -> do_find_lowest(space, b, z - 1) + } +} + +fn to_block_positions(space: Space) -> AllBlocks { + use acc, point, index <- dict.fold(space, dict.new()) + use points <- dict.update(acc, index) + case points { + Some(ps) -> [point, ..ps] + None -> [point] + } +} + +fn above_blocks(blocks: AllBlocks) -> BlockTree { + use acc, block, points <- dict.fold(blocks, dict.new()) + use _ <- dict.update(acc, block.index) + { + use above_block, above_points <- dict.filter(blocks) + above_block.index != block.index + && list.any(above_points, fn(p) { list.contains(points, down_one(p)) }) + } + |> dict.keys + |> list.map(fn(b) { b.index }) + |> set.from_list +} + +fn below_blocks(blocktree: BlockTree) -> BlockTree { + use acc, block, _ <- dict.fold(blocktree, dict.new()) + use _ <- dict.update(acc, block) + { + use _, aboves <- dict.filter(blocktree) + set.contains(aboves, block) + } + |> dict.keys + |> set.from_list +} + +fn vulnerable_blocks(below_tree: BlockTree) -> List(Int) { + use block <- list.filter(dict.keys(below_tree)) + use bs <- list.any(dict.values(below_tree)) + !{ set.size(bs) == 0 } && { set.size(set.delete(bs, block)) == 0 } +} + +pub fn part1(input: String) { + let settled_blocks = + input + |> string.split("\n") + |> list.index_map(parse_block) + |> list.sort(compare_blocks) + |> list.fold(dict.new(), find_lowest_level) + + let block_positions = to_block_positions(settled_blocks) + let above_blocks = above_blocks(block_positions) + let below_blocks = below_blocks(above_blocks) + + let vulnerable_blocks = vulnerable_blocks(below_blocks) + + list.length(dict.keys(block_positions)) - list.length(vulnerable_blocks) +} + +fn all_falling_blocks(n: Int, above: BlockTree, below: BlockTree) { + let starting_set = set.insert(set.new(), n) + do_falling_blocks(starting_set, starting_set, above, below) +} + +fn do_falling_blocks( + fallen: Set(Int), + blocks: Set(Int), + above: BlockTree, + below: BlockTree, +) -> Int { + use <- bool.guard(set.size(blocks) == 0, set.size(fallen) - 1) + + let blocks_above = + { + use block <- list.flat_map(set.to_list(blocks)) + let assert Ok(supports) = dict.get(above, block) + use support <- list.filter(set.to_list(supports)) + let assert Ok(supportings) = dict.get(below, support) + use supporting <- list.all(set.to_list(supportings)) + set.contains(fallen, supporting) + } + |> set.from_list() + + set.union(fallen, blocks_above) + |> do_falling_blocks(blocks_above, above, below) +} + +pub fn part2(input: String) { + let settled_blocks = + input + |> string.split("\n") + |> list.index_map(parse_block) + |> list.sort(compare_blocks) + |> list.fold(dict.new(), find_lowest_level) + + let block_positions = to_block_positions(settled_blocks) + let above_blocks = above_blocks(block_positions) + let below_blocks = below_blocks(above_blocks) + + let vulnerable_blocks = vulnerable_blocks(below_blocks) + + use acc, b <- list.fold(vulnerable_blocks, 0) + acc + all_falling_blocks(b, above_blocks, below_blocks) +} + +pub fn main() { + let assert Ok(part) = adglent.get_part() + let assert Ok(input) = adglent.get_input("22") + case part { + First -> + part1(input) + |> adglent.inspect + |> io.println + Second -> + part2(input) + |> adglent.inspect + |> io.println + } +} diff --git a/gleam/aoc2023/src/day23/.gitignore b/gleam/aoc2023/src/day23/.gitignore new file mode 100644 index 0000000..ae40cea --- /dev/null +++ b/gleam/aoc2023/src/day23/.gitignore @@ -0,0 +1 @@ +input.txt \ No newline at end of file diff --git a/gleam/aoc2023/src/day23/solve.gleam b/gleam/aoc2023/src/day23/solve.gleam new file mode 100644 index 0000000..e1fe638 --- /dev/null +++ b/gleam/aoc2023/src/day23/solve.gleam @@ -0,0 +1,194 @@ +import adglent.{First, Second} +import gleam/int +import gleam/io +import gleam/dict.{type Dict} +import gleam/list +import gleam/option.{type Option, None, Some} +import gleam/string +import gleam/set.{type Set} +import gleam/bool +import utilities/array2d.{type Array2D, type Posn, Posn} + +type Path { + Unknown + Straight + Junction +} + +type Route { + Route(to: Posn, distance: Int) +} + +fn append_to_key(v: Option(List(a)), new: a) -> List(a) { + case v { + None -> [new] + Some(xs) -> [new, ..xs] + } +} + +fn first_parse_path(c: String) -> Result(Path, Nil) { + case c { + "#" -> Error(Nil) + _ -> Ok(Unknown) + } +} + +fn junction_neighbors(p: Posn) -> List(Posn) { + [Posn(..p, r: p.r + 1), Posn(..p, c: p.c + 1)] +} + +fn mark_junctions(trails: Array2D(Path)) -> Array2D(Path) { + use trail, _ <- dict.map_values(trails) + + let valid_neighbors = + trail + |> array2d.ortho_neighbors + |> list.filter(dict.has_key(trails, _)) + + case list.length(valid_neighbors) { + 2 -> Straight + _ -> Junction + } +} + +fn start_walking_to_next_junction( + start: Posn, + next: Posn, + trails: Array2D(Path), +) { + let seen = + set.new() + |> set.insert(start) + |> set.insert(next) + walk_to_next_junction(start, next, 1, seen, trails) +} + +fn walk_to_next_junction( + start: Posn, + current: Posn, + length: Int, + seen: Set(Posn), + trails: Array2D(Path), +) -> #(Posn, Route) { + let assert [next] = + current + |> array2d.ortho_neighbors + |> list.filter(fn(n) { dict.has_key(trails, n) && !set.contains(seen, n) }) + + case dict.get(trails, next) { + Ok(Junction) -> #(start, Route(to: next, distance: length + 1)) + _ -> { + let seen = set.insert(seen, current) + walk_to_next_junction(start, next, { length + 1 }, seen, trails) + } + } +} + +fn find_routes(junctions, trails) { + use junction <- list.flat_map(junctions) + use neighbor <- list.filter_map(junction_neighbors(junction)) + case dict.has_key(trails, neighbor) { + True -> Ok(start_walking_to_next_junction(junction, neighbor, trails)) + False -> Error(Nil) + } +} + +fn generate_routes( + junctions: List(Posn), + trails: Array2D(Path), +) -> Dict(Posn, List(Route)) { + use acc, #(from, route) <- list.fold( + find_routes(junctions, trails), + dict.new(), + ) + dict.update(acc, from, append_to_key(_, route)) +} + +fn generate_2way_routes( + junctions: List(Posn), + trails: Array2D(Path), +) -> Dict(Posn, List(Route)) { + use acc, #(from, route) <- list.fold( + find_routes(junctions, trails), + dict.new(), + ) + acc + |> dict.update(from, append_to_key(_, route)) + |> dict.update(route.to, append_to_key(_, Route(from, route.distance))) +} + +fn dfs(routes, from, to) { + let seen = set.insert(set.new(), from) + do_dfs(routes, from, to, 0, seen) +} + +fn do_dfs( + routes: Dict(Posn, List(Route)), + from: Posn, + to: Posn, + acc: Int, + seen: Set(Posn), +) -> Int { + use <- bool.guard(to == from, acc) + + let assert Ok(all_routes) = dict.get(routes, from) + let neighbors = list.filter(all_routes, fn(r) { !set.contains(seen, r.to) }) + + case neighbors { + [] -> 0 + neighbors -> + list.fold(neighbors, acc, fn(inner_acc, n) { + let score = + do_dfs(routes, n.to, to, acc + n.distance, set.insert(seen, n.to)) + int.max(score, inner_acc) + }) + } +} + +fn solve_using( + input: String, + using: fn(List(Posn), Dict(Posn, Path)) -> Dict(Posn, List(Route)), +) -> Int { + let min_row = 0 + let max_row = list.length(string.split(input, "\n")) - 1 + + let trails = + input + |> array2d.parse_grid_using(first_parse_path) + |> mark_junctions + + let junctions = + trails + |> dict.filter(fn(_, v) { v == Junction }) + |> dict.keys + + let assert Ok(start) = list.find(junctions, fn(j) { j.r == min_row }) + let assert Ok(end) = list.find(junctions, fn(j) { j.r == max_row }) + + let routes = using(junctions, trails) + + dfs(routes, start, end) +} + +pub fn part1(input: String) { + solve_using(input, generate_routes) +} + +pub fn part2(input: String) { + solve_using(input, generate_2way_routes) +} + +pub fn main() { + let assert Ok(part) = adglent.get_part() + let assert Ok(input) = adglent.get_input("23") + case part { + First -> + part1(input) + |> adglent.inspect + |> io.println + Second -> + part2(input) + |> adglent.inspect + |> io.println + } +} diff --git a/gleam/aoc2023/src/day3/.gitignore b/gleam/aoc2023/src/day3/.gitignore new file mode 100644 index 0000000..ae40cea --- /dev/null +++ b/gleam/aoc2023/src/day3/.gitignore @@ -0,0 +1 @@ +input.txt \ No newline at end of file diff --git a/gleam/aoc2023/src/day3/solve.gleam b/gleam/aoc2023/src/day3/solve.gleam new file mode 100644 index 0000000..ad975aa --- /dev/null +++ b/gleam/aoc2023/src/day3/solve.gleam @@ -0,0 +1,180 @@ +import adglent.{First, Second} +import gleam/io +import gleam/dict.{type Dict} +import gleam/string +import gleam/list +import gleam/int +import gleam/order.{type Order, Eq} + +type Coord { + Coord(x: Int, y: Int) +} + +type SymbolKind { + Gear + SomethingElse +} + +type Symbol { + Number(Int) + Symbol(SymbolKind) + Empty +} + +type Board = + Dict(Coord, Symbol) + +type Cell { + Cell(coord: Coord, symbol: Symbol) +} + +type Part { + Part(coords: List(Coord), part_number: Int) +} + +fn to_symbol(c: String) -> Symbol { + case int.parse(c), c { + Ok(n), _ -> Number(n) + _, "." -> Empty + _, "*" -> Symbol(Gear) + _, _ -> Symbol(SomethingElse) + } +} + +fn to_board(input: String) -> Board { + { + use y, r <- list.index_map(string.split(input, "\n")) + use x, c <- list.index_map(string.to_graphemes(r)) + #(Coord(x, y), to_symbol(c)) + } + |> list.flatten() + |> dict.from_list() +} + +fn cell_compare(a: Cell, b: Cell) -> Order { + case int.compare(a.coord.y, b.coord.y) { + Eq -> int.compare(a.coord.x, b.coord.x) + other -> other + } +} + +fn find_all_part_digits(b: Board) -> List(Cell) { + b + |> dict.filter(fn(_, v) { + case v { + Number(_) -> True + _ -> False + } + }) + |> dict.to_list() + |> list.map(fn(tup) { Cell(tup.0, tup.1) }) + |> list.sort(cell_compare) +} + +fn to_parts(cells: List(Cell)) -> List(Part) { + do_parts(cells, []) +} + +fn do_parts(cells: List(Cell), parts: List(Part)) -> List(Part) { + case cells { + [] -> parts + [Cell(next, Number(n)), ..t] -> { + case parts { + [] -> do_parts(t, [Part([next], n), ..parts]) + [Part([prev, ..] as coords, n0), ..rest_parts] -> + case { next.x - prev.x }, { next.y - prev.y } { + 1, 0 -> + do_parts(t, [Part([next, ..coords], n0 * 10 + n), ..rest_parts]) + _, _ -> do_parts(t, [Part([next], n), ..parts]) + } + _ -> panic + } + } + _ -> panic + } +} + +fn all_neighbors(c: Coord) -> List(Coord) { + use dx <- list.flat_map([-1, 0, 1]) + use dy <- list.filter_map([-1, 0, 1]) + case dx, dy { + 0, 0 -> Error(Nil) + _, _ -> Ok(Coord(c.x + dx, c.y + dy)) + } +} + +fn sum_valid_parts(acc: Int, part: Part, board: Board) -> Int { + let neighbors = + part.coords + |> list.flat_map(all_neighbors) + |> list.unique() + + let sym = [Ok(Symbol(Gear)), Ok(Symbol(SomethingElse))] + case list.any(neighbors, fn(c) { list.contains(sym, dict.get(board, c)) }) { + True -> acc + part.part_number + False -> acc + } +} + +pub fn part1(input: String) -> Int { + let board = to_board(input) + + board + |> find_all_part_digits + |> to_parts + |> list.fold(0, fn(acc, p) { sum_valid_parts(acc, p, board) }) +} + +fn to_part_with_neighbors(part: Part) -> Part { + part.coords + |> list.flat_map(all_neighbors) + |> list.unique + |> Part(part.part_number) +} + +fn find_part_numbers_near_gear(gear: Coord, parts: List(Part)) -> List(Int) { + use part <- list.filter_map(parts) + case list.contains(part.coords, gear) { + True -> Ok(part.part_number) + False -> Error(Nil) + } +} + +fn to_sum_of_gear_ratios(adjacent_parts: List(List(Int))) -> Int { + use acc, ps <- list.fold(adjacent_parts, 0) + case ps { + [p1, p2] -> acc + p1 * p2 + _ -> acc + } +} + +pub fn part2(input: String) -> Int { + let board = to_board(input) + + let parts = + board + |> find_all_part_digits + |> to_parts + |> list.map(to_part_with_neighbors) + + board + |> dict.filter(fn(_, v) { v == Symbol(Gear) }) + |> dict.keys + |> list.map(find_part_numbers_near_gear(_, parts)) + |> to_sum_of_gear_ratios +} + +pub fn main() { + let assert Ok(part) = adglent.get_part() + let assert Ok(input) = adglent.get_input("3") + case part { + First -> + part1(input) + |> adglent.inspect + |> io.println + Second -> + part2(input) + |> adglent.inspect + |> io.println + } +} diff --git a/gleam/aoc2023/src/day4/.gitignore b/gleam/aoc2023/src/day4/.gitignore new file mode 100644 index 0000000..ae40cea --- /dev/null +++ b/gleam/aoc2023/src/day4/.gitignore @@ -0,0 +1 @@ +input.txt \ No newline at end of file diff --git a/gleam/aoc2023/src/day4/solve.gleam b/gleam/aoc2023/src/day4/solve.gleam new file mode 100644 index 0000000..34d6098 --- /dev/null +++ b/gleam/aoc2023/src/day4/solve.gleam @@ -0,0 +1,98 @@ +import adglent.{First, Second} +import gleam/bool +import gleam/dict.{type Dict} +import gleam/int +import gleam/io +import gleam/list +import gleam/option.{None, Some} +import gleam/result +import gleam/set.{type Set} +import gleam/string + +type Card { + Card(number: Int, winners: Int) +} + +fn numbers_to_set(str: String) -> Set(Int) { + str + |> string.split(" ") + |> list.map(int.parse) + |> result.values() + |> set.from_list() +} + +fn parse_card(card: String) -> Card { + let assert Ok(#("Card" <> n_str, rest)) = string.split_once(card, ": ") + let assert Ok(#(winning_str, has_str)) = string.split_once(rest, " | ") + let assert Ok(n) = int.parse(string.trim(n_str)) + + let winning = numbers_to_set(winning_str) + let has = numbers_to_set(has_str) + let winners = set.size(set.intersection(winning, has)) + + Card(number: n, winners: winners) +} + +fn win_points(n: Int) { + bool.guard(n < 2, n, fn() { 2 * win_points(n - 1) }) +} + +pub fn part1(input: String) { + use acc, c <- list.fold(string.split(input, "\n"), 0) + c + |> parse_card + |> fn(c: Card) { win_points(c.winners) } + |> int.add(acc) +} + +fn win_more_cards(cards: List(String), count: Dict(Int, Int)) { + case cards { + [] -> + count + |> dict.values + |> int.sum + [raw_card, ..rest] -> { + let card = parse_card(raw_card) + case card.winners { + 0 -> win_more_cards(rest, count) + n -> win_more_cards(rest, update_counts(n, card, count)) + } + } + } +} + +fn update_counts(n: Int, card: Card, count: Dict(Int, Int)) -> Dict(Int, Int) { + let assert Ok(bonus) = dict.get(count, card.number) + use acc, n <- list.fold(list.range(card.number + 1, card.number + n), count) + use c <- dict.update(acc, n) + case c { + Some(i) -> i + bonus + None -> panic as "won a card that doesn't exist in the card pile" + } +} + +pub fn part2(input: String) { + let cards = string.split(input, "\n") + + let count = + list.range(1, list.length(cards)) + |> list.map(fn(n) { #(n, 1) }) + |> dict.from_list() + + win_more_cards(cards, count) +} + +pub fn main() { + let assert Ok(part) = adglent.get_part() + let assert Ok(input) = adglent.get_input("4") + case part { + First -> + part1(input) + |> adglent.inspect + |> io.println + Second -> + part2(input) + |> adglent.inspect + |> io.println + } +} diff --git a/gleam/aoc2023/src/day5/.gitignore b/gleam/aoc2023/src/day5/.gitignore new file mode 100644 index 0000000..ae40cea --- /dev/null +++ b/gleam/aoc2023/src/day5/.gitignore @@ -0,0 +1 @@ +input.txt \ No newline at end of file diff --git a/gleam/aoc2023/src/day5/solve.gleam b/gleam/aoc2023/src/day5/solve.gleam new file mode 100644 index 0000000..7c05310 --- /dev/null +++ b/gleam/aoc2023/src/day5/solve.gleam @@ -0,0 +1,162 @@ +import adglent.{First, Second} +import gleam/io +import gleam/string +import gleam/result +import gleam/list.{Continue, Stop} +import gleam/int +import gleam/function + +// Types ------------------------------------------------------------------------------------------- + +pub type Almanac { + Almanac(seeds: List(Int), mappers: List(Mapper)) +} + +pub type MappingRange { + MRange(start: Int, end: Int, offset: Int) +} + +pub type SeedRange { + SRange(start: Int, end: Int) +} + +type Mapper = + List(MappingRange) + +// Parsing ----------------------------------------------------------------------------------------- + +fn parse_input(input: String) { + let assert ["seeds: " <> raw_seeds, ..raw_mappers] = + string.split(input, on: "\n\n") + + let seeds = string_to_int_list(raw_seeds) + let mappers = + list.map( + raw_mappers, + function.compose(string.split(_, on: "\n"), parse_mapper), + ) + Almanac(seeds, mappers) +} + +fn string_to_int_list(str: String) { + str + |> string.split(on: " ") + |> list.map(int.parse) + |> result.values +} + +fn parse_mapper(strs: List(String)) -> Mapper { + let assert [_, ..raw_ranges] = strs + list.map(raw_ranges, parse_mrange) + |> list.sort(fn(a, b) { int.compare(a.start, b.start) }) +} + +fn parse_mrange(str: String) -> MappingRange { + let assert [destination, source, range_width] = string_to_int_list(str) + MRange(source, source + range_width - 1, destination - source) +} + +// Part 1 ------------------------------------------------------------------------------------------ + +pub fn part1(input: String) { + let Almanac(seeds, mappers) = parse_input(input) + + list.map(seeds, list.fold(over: mappers, from: _, with: correspond)) + |> list.reduce(int.min) + |> result.unwrap(0) + |> string.inspect +} + +fn correspond(n: Int, mapper: Mapper) { + use acc, mrange <- list.fold_until(over: mapper, from: n) + case mrange.start <= acc && acc <= mrange.end { + True -> Stop(acc + mrange.offset) + False -> Continue(acc) + } +} + +// Part 2 ------------------------------------------------------------------------------------------ + +pub fn part2(input: String) { + let Almanac(seeds, mappers) = parse_input(input) + + let assert [SRange(answer, _), ..] = + seeds + |> list.sized_chunk(into: 2) + |> list.map(fn(chunk) { + let assert [start, length] = chunk + [SRange(start, start + length - 1)] + |> remap_all_seed_ranges(mappers) + }) + |> list.flatten() + |> list.sort(fn(a, b) { int.compare(a.start, b.start) }) + + string.inspect(answer) +} + +fn remap_all_seed_ranges(srs: List(SeedRange), mappers: List(Mapper)) { + case mappers { + [] -> srs + [mapper, ..rest] -> + list.flat_map(srs, remap_range(_, mapper)) + |> remap_all_seed_ranges(rest) + } +} + +fn remap_range(r: SeedRange, mapper: Mapper) -> List(SeedRange) { + do_remap_range(r, mapper, []) +} + +fn transform_range(r: SeedRange, mapper: MappingRange) -> SeedRange { + SRange(r.start + mapper.offset, r.end + mapper.offset) +} + +fn do_remap_range(r: SeedRange, mapper: Mapper, acc: List(SeedRange)) { + case mapper { + // no more mappings -> no mapping covers this range + [] -> [r, ..acc] + // range is to the left of current mapping -> no mapping covers this range + [m, ..] if r.end < m.start -> [r, ..acc] + // range is to the right of current mapping -> move to next mapping + [m, ..ms] if r.start > m.end -> do_remap_range(r, ms, acc) + // range is fully inside mapping -> range is transformed + [m, ..] if r.start >= m.start && r.end <= m.end -> [ + transform_range(r, m), + ..acc + ] + // range overlaps start but not end -> left side not transformed, right side transformed + [m, ..] if r.start < m.start && r.end <= m.end -> [ + SRange(r.start, m.start - 1), + transform_range(SRange(m.start, r.end), m), + ..acc + ] + // range overlaps end but not start -> left side transformed, right side moves to next mapping + [m, ..ms] if r.start >= m.start && r.end > m.end -> + do_remap_range(SRange(m.end + 1, r.end), ms, [ + transform_range(SRange(r.start, m.end), m), + ..acc + ]) + // mapping is fully inside range -> left not transformed, middle transformed, right to next + [m, ..ms] -> + do_remap_range(SRange(m.end + 1, r.end), ms, [ + SRange(r.start, m.start - 1), + transform_range(SRange(m.start, m.end), m), + ..acc + ]) + } +} + +pub fn main() { + let assert Ok(part) = adglent.get_part() + let assert Ok(input) = adglent.get_input("5") + case part { + First -> + part1(input) + |> adglent.inspect + |> io.println + Second -> + part2(input) + |> adglent.inspect + |> io.println + } +} diff --git a/gleam/aoc2023/src/day6/.gitignore b/gleam/aoc2023/src/day6/.gitignore new file mode 100644 index 0000000..ae40cea --- /dev/null +++ b/gleam/aoc2023/src/day6/.gitignore @@ -0,0 +1 @@ +input.txt \ No newline at end of file diff --git a/gleam/aoc2023/src/day6/solve.gleam b/gleam/aoc2023/src/day6/solve.gleam new file mode 100644 index 0000000..88044c4 --- /dev/null +++ b/gleam/aoc2023/src/day6/solve.gleam @@ -0,0 +1,85 @@ +import adglent.{First, Second} +import gleam/io +import gleam/string +import gleam/int +import gleam/list +import gleam/result + +type Race { + Race(time: Int, distance: Int) +} + +fn parse_with_bad_kerning(input: String) { + input + |> string.split("\n") + |> list.map(fn(str) { + str + |> string.split(" ") + |> list.map(int.parse) + |> result.values + }) + |> list.transpose + |> list.map(fn(ns) { + let assert [t, d] = ns + Race(t, d) + }) +} + +fn find_bound(race: Race, button_time: Int, step: Int) { + let travel_time = race.time - button_time + case button_time * travel_time > race.distance { + True -> button_time + False -> find_bound(race, button_time + step, step) + } +} + +fn lower_bound(race: Race) { + find_bound(race, 1, 1) +} + +fn upper_bound(race: Race) { + find_bound(race, race.time, -1) +} + +pub fn part1(input: String) { + { + use acc, race <- list.fold(parse_with_bad_kerning(input), 1) + acc * { upper_bound(race) - lower_bound(race) + 1 } + } + |> string.inspect +} + +fn parse_properly(input: String) { + input + |> string.replace(" ", "") + |> string.split("\n") + |> list.flat_map(string.split(_, ":")) + |> list.map(int.parse) + |> result.values +} + +pub fn part2(input: String) { + let assert [time, distance] = + input + |> parse_properly + + let race = Race(time, distance) + + upper_bound(race) - lower_bound(race) + 1 + |> string.inspect +} + +pub fn main() { + let assert Ok(part) = adglent.get_part() + let assert Ok(input) = adglent.get_input("6") + case part { + First -> + part1(input) + |> adglent.inspect + |> io.println + Second -> + part2(input) + |> adglent.inspect + |> io.println + } +} diff --git a/gleam/aoc2023/src/day7/.gitignore b/gleam/aoc2023/src/day7/.gitignore new file mode 100644 index 0000000..ae40cea --- /dev/null +++ b/gleam/aoc2023/src/day7/.gitignore @@ -0,0 +1 @@ +input.txt \ No newline at end of file diff --git a/gleam/aoc2023/src/day7/solve.gleam b/gleam/aoc2023/src/day7/solve.gleam new file mode 100644 index 0000000..4454883 --- /dev/null +++ b/gleam/aoc2023/src/day7/solve.gleam @@ -0,0 +1,140 @@ +import adglent.{First, Second} +import gleam/bool +import gleam/function +import gleam/int +import gleam/io +import gleam/list +import gleam/order.{type Order, Eq, Lt} +import gleam/string + +// Types ------------------------------------------------------------------------------------------- + +type Hand { + Hand(cards: List(Int), wager: Int) +} + +// Common functions -------------------------------------------------------------------------------- + +fn parse_hand(str: String) -> Hand { + let assert [cards, wager] = string.split(str, " ") + let cards = + string.to_graphemes(cards) + |> list.map(card_rank) + let assert Ok(wager) = int.parse(wager) + + Hand(cards, wager) +} + +fn classify_hand(hand: Hand) -> Int { + case list.length(list.unique(hand.cards)), card_counts(hand) { + 1, _ -> 8 + 2, [1, 4] -> 7 + 2, [2, 3] -> 6 + 3, [1, 1, 3] -> 5 + 3, [1, 2, 2] -> 4 + 4, _ -> 3 + 5, _ -> 2 + _, _ -> 1 + } +} + +fn card_counts(hand: Hand) { + hand.cards + |> list.sort(int.compare) + |> list.chunk(function.identity) + |> list.map(list.length) + |> list.sort(int.compare) +} + +fn card_rank(card: String) -> Int { + case int.parse(card), card { + Ok(n), _ -> n + _, "A" -> 14 + _, "K" -> 13 + _, "Q" -> 12 + _, "J" -> 11 + _, "T" -> 10 + _, _ -> 1 + } +} + +fn compare_hands(hand1: Hand, hand2: Hand, using: fn(Hand) -> Int) -> Order { + case int.compare(using(hand1), using(hand2)) { + Eq -> compare_top_card(hand1.cards, hand2.cards) + other -> other + } +} + +fn compare_top_card(cards1: List(Int), cards2: List(Int)) -> Order { + use <- bool.guard(cards1 == [] || cards2 == [], Eq) + let assert [c1, ..rest1] = cards1 + let assert [c2, ..rest2] = cards2 + case int.compare(c1, c2) { + Eq -> compare_top_card(rest1, rest2) + other -> other + } +} + +fn part(input: String, comparator: fn(Hand, Hand) -> Order) { + input + |> string.split("\n") + |> list.map(parse_hand) + |> list.sort(comparator) + |> list.index_map(fn(i, h) { { i + 1 } * h.wager }) + |> int.sum + |> string.inspect +} + +// Part 1 ------------------------------------------------------------------------------------------ + +pub fn part1(input: String) { + part(input, compare_without_wilds) +} + +fn compare_without_wilds(hand1: Hand, hand2: Hand) { + compare_hands(hand1, hand2, classify_hand) +} + +// Part 2 ------------------------------------------------------------------------------------------ + +pub fn part2(input: String) { + part(string.replace(input, "J", "*"), compare_hands_considering_jokers) +} + +fn find_best_joker_substitution(hand: Hand) { + use acc, card <- list.fold(list.range(2, 14), Hand([], 0)) + let subbed_cards = { + use c <- list.map(hand.cards) + case c { + 1 -> card + other -> other + } + } + let subbed_hand = Hand(..hand, cards: subbed_cards) + case compare_hands(acc, subbed_hand, classify_hand) { + Lt -> subbed_hand + _ -> acc + } +} + +fn compare_hands_considering_jokers(hand1: Hand, hand2: Hand) -> Order { + use hand <- compare_hands(hand1, hand2) + hand + |> find_best_joker_substitution + |> classify_hand +} + +pub fn main() { + let assert Ok(part) = adglent.get_part() + let assert Ok(input) = adglent.get_input("7") + case part { + First -> + part1(input) + |> adglent.inspect + |> io.println + Second -> + part2(input) + |> adglent.inspect + |> io.println + } +} diff --git a/gleam/aoc2023/src/day8/.gitignore b/gleam/aoc2023/src/day8/.gitignore new file mode 100644 index 0000000..ae40cea --- /dev/null +++ b/gleam/aoc2023/src/day8/.gitignore @@ -0,0 +1 @@ +input.txt \ No newline at end of file diff --git a/gleam/aoc2023/src/day8/solve.gleam b/gleam/aoc2023/src/day8/solve.gleam new file mode 100644 index 0000000..6b36e2d --- /dev/null +++ b/gleam/aoc2023/src/day8/solve.gleam @@ -0,0 +1,91 @@ +import adglent.{First, Second} +import gleam/bool +import gleam/dict.{type Dict} +import gleam/io +import gleam/iterator.{type Iterator, Next} +import gleam/list +import gleam/option.{Some} +import gleam/string +import gleam/regex.{type Match, Match} +import gleam_community/maths/arithmetics + +type Paths { + Paths(to_left: String, to_right: String) +} + +type Maze = + Dict(String, Paths) + +fn parse(input: String) -> #(Iterator(String), Dict(String, Paths)) { + let assert [directions_str, maze_str] = string.split(input, "\n\n") + + let directions = + directions_str + |> string.to_graphemes() + |> iterator.from_list + |> iterator.cycle + + let assert Ok(re) = regex.from_string("(...) = \\((...), (...)\\)") + let maze = + maze_str + |> string.split("\n") + |> list.map(fn(str) { + let assert [Match(submatches: [Some(name), Some(left), Some(right)], ..)] = + regex.scan(re, str) + #(name, Paths(left, right)) + }) + |> dict.from_list + + #(directions, maze) +} + +fn to_next_step( + current: String, + stop_at: String, + count: Int, + directions: Iterator(String), + maze: Maze, +) -> Int { + use <- bool.guard(string.ends_with(current, stop_at), count) + let assert Next(next_direction, rest_directions) = iterator.step(directions) + let assert Ok(paths) = dict.get(maze, current) + case next_direction { + "L" -> paths.to_left + "R" -> paths.to_right + _ -> panic as "bad direction" + } + |> to_next_step(stop_at, count + 1, rest_directions, maze) +} + +pub fn part1(input: String) -> Int { + let #(directions, maze) = parse(input) + + to_next_step("AAA", "ZZZ", 0, directions, maze) +} + +pub fn part2(input: String) -> Int { + let #(directions, maze) = parse(input) + + use acc, name <- list.fold(dict.keys(maze), 1) + case string.ends_with(name, "A") { + False -> acc + True -> + to_next_step(name, "Z", 0, directions, maze) + |> arithmetics.lcm(acc) + } +} + +pub fn main() { + let assert Ok(part) = adglent.get_part() + let assert Ok(input) = adglent.get_input("8") + case part { + First -> + part1(input) + |> adglent.inspect + |> io.println + Second -> + part2(input) + |> adglent.inspect + |> io.println + } +} diff --git a/gleam/aoc2023/src/day9/.gitignore b/gleam/aoc2023/src/day9/.gitignore new file mode 100644 index 0000000..ae40cea --- /dev/null +++ b/gleam/aoc2023/src/day9/.gitignore @@ -0,0 +1 @@ +input.txt \ No newline at end of file diff --git a/gleam/aoc2023/src/day9/solve.gleam b/gleam/aoc2023/src/day9/solve.gleam new file mode 100644 index 0000000..a2cc7ae --- /dev/null +++ b/gleam/aoc2023/src/day9/solve.gleam @@ -0,0 +1,70 @@ +import adglent.{First, Second} +import gleam/io +import gleam/list +import gleam/string +import gleam/int + +fn parse(input: String, backwards backwards: Bool) -> List(List(Int)) { + use line <- list.map(string.split(input, "\n")) + use n_str <- list.map(maybe_backwards(string.split(line, " "), backwards)) + let assert Ok(n) = int.parse(n_str) + n +} + +fn maybe_backwards(xs: List(a), backwards: Bool) -> List(a) { + case backwards { + False -> list.reverse(xs) + True -> xs + } +} + +fn is_constant(ns: List(Int)) -> Bool { + case list.unique(ns) { + [_] -> True + _ -> False + } +} + +fn take_derivative(ns: List(Int)) -> List(Int) { + ns + |> list.window_by_2 + |> list.map(fn(tup) { tup.0 - tup.1 }) +} + +fn extrapolate(ns: List(Int)) { + case is_constant(ns), ns { + True, [n, ..] -> n + False, [n, ..] -> n + extrapolate(take_derivative(ns)) + _, _ -> panic as "list empty when it shouldn't be" + } +} + +fn part(input: String, backwards backwards: Bool) { + input + |> parse(backwards: backwards) + |> list.fold(0, fn(acc, ns) { extrapolate(ns) + acc }) + |> string.inspect +} + +pub fn part1(input: String) { + part(input, backwards: False) +} + +pub fn part2(input: String) { + part(input, backwards: True) +} + +pub fn main() { + let assert Ok(part) = adglent.get_part() + let assert Ok(input) = adglent.get_input("9") + case part { + First -> + part1(input) + |> adglent.inspect + |> io.println + Second -> + part2(input) + |> adglent.inspect + |> io.println + } +} diff --git a/gleam/aoc2023/src/utilities/array2d.gleam b/gleam/aoc2023/src/utilities/array2d.gleam new file mode 100644 index 0000000..8538129 --- /dev/null +++ b/gleam/aoc2023/src/utilities/array2d.gleam @@ -0,0 +1,74 @@ +import gleam/list +import gleam/dict.{type Dict} +import gleam/string +import gleam/int +import gleam/result + +pub type Posn { + Posn(r: Int, c: Int) +} + +pub type Array2D(a) = + Dict(Posn, a) + +pub fn add_posns(p1: Posn, p2: Posn) -> Posn { + case p1, p2 { + Posn(r1, c1), Posn(r2, c2) -> Posn(r1 + r2, c1 + c2) + } +} + +pub fn ortho_neighbors(p: Posn) -> List(Posn) { + let Posn(r, c) = p + [Posn(r + 1, c), Posn(r - 1, c), Posn(r, c + 1), Posn(r, c - 1)] +} + +pub fn to_2d_array(xss: List(List(a))) -> Array2D(a) { + to_2d_array_using(xss, fn(x) { Ok(x) }) +} + +pub fn to_2d_array_using( + xss: List(List(a)), + f: fn(a) -> Result(b, Nil), +) -> Array2D(b) { + { + use r, row <- list.index_map(xss) + use c, cell <- list.index_map(row) + case f(cell) { + Ok(contents) -> Ok(#(Posn(r, c), contents)) + Error(Nil) -> Error(Nil) + } + } + |> list.flatten + |> result.values + |> dict.from_list +} + +pub fn to_2d_intarray(xss: List(List(String))) -> Array2D(Int) { + { + use r, row <- list.index_map(xss) + use c, cell <- list.index_map(row) + let assert Ok(n) = int.parse(cell) + #(Posn(r, c), n) + } + |> list.flatten + |> dict.from_list +} + +pub fn to_list_of_lists(str: String) -> List(List(String)) { + str + |> string.split("\n") + |> list.map(string.to_graphemes) +} + +pub fn parse_grid(str: String) -> Array2D(String) { + parse_grid_using(str, fn(x) { Ok(x) }) +} + +pub fn parse_grid_using( + str: String, + f: fn(String) -> Result(a, Nil), +) -> Array2D(a) { + str + |> to_list_of_lists + |> to_2d_array_using(f) +} diff --git a/gleam/aoc2023/src/utilities/memo.gleam b/gleam/aoc2023/src/utilities/memo.gleam new file mode 100644 index 0000000..b06d8fd --- /dev/null +++ b/gleam/aoc2023/src/utilities/memo.gleam @@ -0,0 +1,57 @@ +import gleam/dict.{type Dict} +import gleam/otp/actor.{type Next, Continue, Stop} +import gleam/erlang/process.{type Subject, Normal} +import gleam/option.{None} + +const timeout = 1000 + +type Message(k, v) { + Shutdown + Get(key: k, client: Subject(Result(v, Nil))) + Set(key: k, value: v) +} + +type Server(k, v) = + Subject(Message(k, v)) + +pub opaque type Cache(k, v) { + Cache(server: Server(k, v)) +} + +fn handle_message( + message: Message(k, v), + dict: Dict(k, v), +) -> Next(Message(k, v), Dict(k, v)) { + case message { + Shutdown -> Stop(Normal) + Get(key, client) -> { + process.send(client, dict.get(dict, key)) + Continue(dict, None) + } + Set(key, value) -> Continue(dict.insert(dict, key, value), None) + } +} + +pub fn create(apply fun: fn(Cache(k, v)) -> t) -> t { + let assert Ok(server) = actor.start(dict.new(), handle_message) + let result = fun(Cache(server)) + process.send(server, Shutdown) + result +} + +pub fn set(in cache: Cache(k, v), for key: k, insert value: v) -> Nil { + process.send(cache.server, Set(key, value)) +} + +pub fn get(from cache: Cache(k, v), fetch key: k) -> Result(v, Nil) { + process.call(cache.server, fn(c) { Get(key, c) }, timeout) +} + +pub fn memoize(with cache: Cache(k, v), this key: k, apply fun: fn() -> v) -> v { + let result = case get(from: cache, fetch: key) { + Ok(value) -> value + Error(Nil) -> fun() + } + set(in: cache, for: key, insert: result) + result +} diff --git a/gleam/aoc2023/src/utilities/prioqueue.gleam b/gleam/aoc2023/src/utilities/prioqueue.gleam new file mode 100644 index 0000000..abf21b9 --- /dev/null +++ b/gleam/aoc2023/src/utilities/prioqueue.gleam @@ -0,0 +1,64 @@ +//adapted from https://github.com/byronanderson/adventofcode2021/blob/main/gleam_advent/src/priority_queue.gleam + +import gleam/dict.{type Dict} + +type Ref + +@external(erlang, "erlang", "make_ref") +fn make_ref() -> Ref + +type PQueue(a) + +pub opaque type PriorityQueue(a) { + PriorityQueue(queue: PQueue(#(a, Ref)), refs: Dict(a, Ref)) +} + +type OutResult(a) { + Empty + Value(a, Int) +} + +@external(erlang, "pqueue2", "new") +fn new_() -> PQueue(a) + +@external(erlang, "pqueue2", "in") +fn insert_(item: a, prio: Int, queue: PQueue(a)) -> PQueue(a) + +@external(erlang, "pqueue2", "pout") +fn pop_(queue: PQueue(a)) -> #(OutResult(a), PQueue(a)) + +pub fn new() -> PriorityQueue(a) { + PriorityQueue(queue: new_(), refs: dict.new()) +} + +pub fn insert( + queue: PriorityQueue(a), + value: a, + priority: Int, +) -> PriorityQueue(a) { + let ref = make_ref() + + let refs = + queue.refs + |> dict.insert(value, ref) + + PriorityQueue( + refs: refs, + queue: insert_(#(value, ref), priority, queue.queue), + ) +} + +pub fn pop(queue: PriorityQueue(a)) -> Result(#(a, PriorityQueue(a)), Nil) { + case pop_(queue.queue) { + #(Value(#(value, ref), _priority), pqueue) -> { + let assert Ok(recently_enqueued_ref) = dict.get(queue.refs, value) + case recently_enqueued_ref == ref { + True -> Ok(#(value, PriorityQueue(refs: queue.refs, queue: pqueue))) + False -> pop(PriorityQueue(refs: queue.refs, queue: pqueue)) + } + } + #(Empty, _pqueue) -> { + Error(Nil) + } + } +} diff --git a/gleam/aoc2023/test/aoc2023_test.gleam b/gleam/aoc2023/test/aoc2023_test.gleam new file mode 100644 index 0000000..2b696a4 --- /dev/null +++ b/gleam/aoc2023/test/aoc2023_test.gleam @@ -0,0 +1,5 @@ +import showtime + +pub fn main() { + showtime.main() +} diff --git a/gleam/aoc2023/test/day1/day1_test.gleam b/gleam/aoc2023/test/day1/day1_test.gleam new file mode 100644 index 0000000..374653c --- /dev/null +++ b/gleam/aoc2023/test/day1/day1_test.gleam @@ -0,0 +1,57 @@ +import gleam/list +import showtime/tests/should +import adglent.{type Example, Example} +import day1/solve + +type Problem1AnswerType = + String + +type Problem2AnswerType = + String + +/// Add examples for part 1 here: +/// ```gleam +///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] +/// ``` +const part1_examples: List(Example(Problem1AnswerType)) = [ + Example( + "1abc2 +pqr3stu8vwx +a1b2c3d4e5f +treb7uchet", + "142", + ), +] + +/// Add examples for part 2 here: +/// ```gleam +///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] +/// ``` +const part2_examples: List(Example(Problem2AnswerType)) = [ + Example( + "two1nine +eightwothree +abcone2threexyz +xtwone3four +4nineeightseven2 +zoneight234 +7pqrstsixteen", + "281", + ), +] + +pub fn part1_test() { + part1_examples + |> should.not_equal([]) + use example <- list.map(part1_examples) + solve.part1(example.input) + |> should.equal(example.answer) +} + +pub fn part2_test() { + part2_examples + |> should.not_equal([]) + use example <- list.map(part2_examples) + solve.part2(example.input) + |> should.equal(example.answer) +} diff --git a/gleam/aoc2023/test/day10/day10_test.gleam b/gleam/aoc2023/test/day10/day10_test.gleam new file mode 100644 index 0000000..be9d82e --- /dev/null +++ b/gleam/aoc2023/test/day10/day10_test.gleam @@ -0,0 +1,60 @@ +import gleam/list +import showtime/tests/should +import adglent.{type Example, Example} +import day10/solve + +type Problem1AnswerType = + String + +type Problem2AnswerType = + String + +/// Add examples for part 1 here: +/// ```gleam +///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] +/// ``` +const part1_examples: List(Example(Problem1AnswerType)) = [ + Example( + "7-F7- +.FJ|7 +SJLL7 +|F--J +LJ.LJ", + "8", + ), +] + +/// Add examples for part 2 here: +/// ```gleam +///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] +/// ``` +const part2_examples: List(Example(Problem2AnswerType)) = [ + Example( + "........... +.S-------7. +.|F-----7|. +.||OOOOO||. +.||OOOOO||. +.|L-7OF-J|. +.|II|O|II|. +.L--JOL--J. +.....O.....", + "4", + ), +] + +pub fn part1_test() { + part1_examples + |> should.not_equal([]) + use example <- list.map(part1_examples) + solve.part1(example.input) + |> should.equal(example.answer) +} + +pub fn part2_test() { + part2_examples + |> should.not_equal([]) + use example <- list.map(part2_examples) + solve.part2(example.input) + |> should.equal(example.answer) +} diff --git a/gleam/aoc2023/test/day11/day11_test.gleam b/gleam/aoc2023/test/day11/day11_test.gleam new file mode 100644 index 0000000..8bb8c06 --- /dev/null +++ b/gleam/aoc2023/test/day11/day11_test.gleam @@ -0,0 +1,66 @@ +import gleam/list +import showtime/tests/should +import adglent.{type Example, Example} +import day11/solve + +type Problem1AnswerType = + String + +type Problem2AnswerType = + String + +/// Add examples for part 1 here: +/// ```gleam +///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] +/// ``` +const part1_examples: List(Example(Problem1AnswerType)) = [ + Example( + "...#...... +.......#.. +#......... +.......... +......#... +.#........ +.........# +.......... +.......#.. +#...#.....", + "374", + ), +] + +/// Add examples for part 2 here: +/// ```gleam +///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] +/// ``` +const part2_examples: List(Example(Problem2AnswerType)) = [ + Example( + "...#...... +.......#.. +#......... +.......... +......#... +.#........ +.........# +.......... +.......#.. +#...#.....", + "8410", + ), +] + +pub fn part1_test() { + part1_examples + |> should.not_equal([]) + use example <- list.map(part1_examples) + solve.part1(example.input) + |> should.equal(example.answer) +} + +pub fn part2_test() { + part2_examples + |> should.not_equal([]) + use example <- list.map(part2_examples) + solve.part2(example.input) + |> should.equal(example.answer) +} diff --git a/gleam/aoc2023/test/day12/day12_test.gleam b/gleam/aoc2023/test/day12/day12_test.gleam new file mode 100644 index 0000000..3daf0e9 --- /dev/null +++ b/gleam/aoc2023/test/day12/day12_test.gleam @@ -0,0 +1,48 @@ +import gleam/list +import showtime/tests/should +import adglent.{type Example, Example} +import day12/solve + +type Problem1AnswerType = + String + +type Problem2AnswerType = + String + +/// Add examples for part 1 here: +/// ```gleam +///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] +/// ``` +const part1_examples: List(Example(Problem1AnswerType)) = [ + Example( + "???.### 1,1,3 +.??..??...?##. 1,1,3 +?#?#?#?#?#?#?#? 1,3,1,6 +????.#...#... 4,1,1 +????.######..#####. 1,6,5 +?###???????? 3,2,1", + "21", + ), +] + +/// Add examples for part 2 here: +/// ```gleam +///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] +/// ``` +const part2_examples: List(Example(Problem2AnswerType)) = [] + +pub fn part1_test() { + part1_examples + |> should.not_equal([]) + use example <- list.map(part1_examples) + solve.part1(example.input) + |> should.equal(example.answer) +} + +pub fn part2_test() { + part2_examples + |> should.not_equal([]) + use example <- list.map(part2_examples) + solve.part2(example.input) + |> should.equal(example.answer) +} diff --git a/gleam/aoc2023/test/day13/day13_test.gleam b/gleam/aoc2023/test/day13/day13_test.gleam new file mode 100644 index 0000000..7c65bed --- /dev/null +++ b/gleam/aoc2023/test/day13/day13_test.gleam @@ -0,0 +1,76 @@ +import gleam/list +import showtime/tests/should +import adglent.{type Example, Example} +import day13/solve + +type Problem1AnswerType = + String + +type Problem2AnswerType = + String + +/// Add examples for part 1 here: +/// ```gleam +///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] +/// ``` +const part1_examples: List(Example(Problem1AnswerType)) = [ + Example( + "#.##..##. +..#.##.#. +##......# +##......# +..#.##.#. +..##..##. +#.#.##.#. + +#...##..# +#....#..# +..##..### +#####.##. +#####.##. +..##..### +#....#..#", + "405", + ), +] + +/// Add examples for part 2 here: +/// ```gleam +///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] +/// ``` +const part2_examples: List(Example(Problem2AnswerType)) = [ + Example( + "#.##..##. +..#.##.#. +##......# +##......# +..#.##.#. +..##..##. +#.#.##.#. + +#...##..# +#....#..# +..##..### +#####.##. +#####.##. +..##..### +#....#..#", + "400", + ), +] + +pub fn part1_test() { + part1_examples + |> should.not_equal([]) + use example <- list.map(part1_examples) + solve.part1(example.input) + |> should.equal(example.answer) +} + +pub fn part2_test() { + part2_examples + |> should.not_equal([]) + use example <- list.map(part2_examples) + solve.part2(example.input) + |> should.equal(example.answer) +} diff --git a/gleam/aoc2023/test/day14/day14_test.gleam b/gleam/aoc2023/test/day14/day14_test.gleam new file mode 100644 index 0000000..8efa74e --- /dev/null +++ b/gleam/aoc2023/test/day14/day14_test.gleam @@ -0,0 +1,66 @@ +import gleam/list +import showtime/tests/should +import adglent.{type Example, Example} +import day14/solve + +type Problem1AnswerType = + String + +type Problem2AnswerType = + String + +/// Add examples for part 1 here: +/// ```gleam +///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] +/// ``` +const part1_examples: List(Example(Problem1AnswerType)) = [ + Example( + "O....#.... +O.OO#....# +.....##... +OO.#O....O +.O.....O#. +O.#..O.#.# +..O..#O..O +.......O.. +#....###.. +#OO..#....", + "136", + ), +] + +/// Add examples for part 2 here: +/// ```gleam +///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] +/// ``` +const part2_examples: List(Example(Problem2AnswerType)) = [ + Example( + "O....#.... +O.OO#....# +.....##... +OO.#O....O +.O.....O#. +O.#..O.#.# +..O..#O..O +.......O.. +#....###.. +#OO..#....", + "64", + ), +] + +pub fn part1_test() { + part1_examples + |> should.not_equal([]) + use example <- list.map(part1_examples) + solve.part1(example.input) + |> should.equal(example.answer) +} + +pub fn part2_test() { + part2_examples + |> should.not_equal([]) + use example <- list.map(part2_examples) + solve.part2(example.input) + |> should.equal(example.answer) +} diff --git a/gleam/aoc2023/test/day15/day15_test.gleam b/gleam/aoc2023/test/day15/day15_test.gleam new file mode 100644 index 0000000..0ecaecc --- /dev/null +++ b/gleam/aoc2023/test/day15/day15_test.gleam @@ -0,0 +1,42 @@ +import gleam/list +import showtime/tests/should +import adglent.{type Example, Example} +import day15/solve + +type Problem1AnswerType = + String + +type Problem2AnswerType = + String + +/// Add examples for part 1 here: +/// ```gleam +///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] +/// ``` +const part1_examples: List(Example(Problem1AnswerType)) = [ + Example("rn=1,cm-,qp=3,cm=2,qp-,pc=4,ot=9,ab=5,pc-,pc=6,ot=7", "1320"), +] + +/// Add examples for part 2 here: +/// ```gleam +///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] +/// ``` +const part2_examples: List(Example(Problem2AnswerType)) = [ + Example("rn=1,cm-,qp=3,cm=2,qp-,pc=4,ot=9,ab=5,pc-,pc=6,ot=7", "145"), +] + +pub fn part1_test() { + part1_examples + |> should.not_equal([]) + use example <- list.map(part1_examples) + solve.part1(example.input) + |> should.equal(example.answer) +} + +pub fn part2_test() { + part2_examples + |> should.not_equal([]) + use example <- list.map(part2_examples) + solve.part2(example.input) + |> should.equal(example.answer) +} diff --git a/gleam/aoc2023/test/day16/day16_test.gleam b/gleam/aoc2023/test/day16/day16_test.gleam new file mode 100644 index 0000000..036504e --- /dev/null +++ b/gleam/aoc2023/test/day16/day16_test.gleam @@ -0,0 +1,66 @@ +import gleam/list +import showtime/tests/should +import adglent.{type Example, Example} +import day16/solve + +type Problem1AnswerType = + Int + +type Problem2AnswerType = + Int + +/// Add examples for part 1 here: +/// ```gleam +///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] +/// ``` +const part1_examples: List(Example(Problem1AnswerType)) = [ + Example( + ".|...\\.... +|.-.\\..... +.....|-... +........|. +.......... +.........\\ +..../.\\\\.. +.-.-/..|.. +.|....-|.\\ +..//.|....", + 46, + ), +] + +/// Add examples for part 2 here: +/// ```gleam +///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] +/// ``` +const part2_examples: List(Example(Problem2AnswerType)) = [ + Example( + ".|...\\.... +|.-.\\..... +.....|-... +........|. +.......... +.........\\ +..../.\\\\.. +.-.-/..|.. +.|....-|.\\ +..//.|....", + 51, + ), +] + +pub fn part1_test() { + part1_examples + |> should.not_equal([]) + use example <- list.map(part1_examples) + solve.part1(example.input) + |> should.equal(example.answer) +} + +pub fn part2_test() { + part2_examples + |> should.not_equal([]) + use example <- list.map(part2_examples) + solve.part2(example.input) + |> should.equal(example.answer) +} diff --git a/gleam/aoc2023/test/day17/day17_test.gleam b/gleam/aoc2023/test/day17/day17_test.gleam new file mode 100644 index 0000000..2ce48e2 --- /dev/null +++ b/gleam/aoc2023/test/day17/day17_test.gleam @@ -0,0 +1,54 @@ +import gleam/list +import showtime/tests/should +import adglent.{type Example, Example} +import day17/solve + +type Problem1AnswerType = + String + +// type Problem2AnswerType = +// String + +/// Add examples for part 1 here: +/// ```gleam +///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] +/// ``` +const part1_examples: List(Example(Problem1AnswerType)) = [ + Example( + "2413432311323 +3215453535623 +3255245654254 +3446585845452 +4546657867536 +1438598798454 +4457876987766 +3637877979653 +4654967986887 +4564679986453 +1224686865563 +2546548887735 +4322674655533", + "102", + ), +] + +// /// ``` +// const part2_examples: List(Example(Problem2AnswerType)) = [] + +/// Add examples for part 2 here: +/// ```gleam +///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] +pub fn part1_test() { + part1_examples + |> should.not_equal([]) + use example <- list.map(part1_examples) + solve.part1(example.input) + |> should.equal(example.answer) +} +// pub fn part2_test() { +// part2_examples +// |> should.not_equal([]) +// use example <- list.map(part2_examples) +// solve.part2(example.input) +// |> should.equal(example.answer) +// } diff --git a/gleam/aoc2023/test/day18/day18_test.gleam b/gleam/aoc2023/test/day18/day18_test.gleam new file mode 100644 index 0000000..7b510c8 --- /dev/null +++ b/gleam/aoc2023/test/day18/day18_test.gleam @@ -0,0 +1,74 @@ +import gleam/list +import showtime/tests/should +import adglent.{type Example, Example} +import day18/solve + +type Problem1AnswerType = + String + +type Problem2AnswerType = + String + +/// Add examples for part 1 here: +/// ```gleam +///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] +/// ``` +const part1_examples: List(Example(Problem1AnswerType)) = [ + Example( + "R 6 (#70c710) +D 5 (#0dc571) +L 2 (#5713f0) +D 2 (#d2c081) +R 2 (#59c680) +D 2 (#411b91) +L 5 (#8ceee2) +U 2 (#caa173) +L 1 (#1b58a2) +U 2 (#caa171) +R 2 (#7807d2) +U 3 (#a77fa3) +L 2 (#015232) +U 2 (#7a21e3)", + "62", + ), +] + +/// Add examples for part 2 here: +/// ```gleam +///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] +/// ``` +const part2_examples: List(Example(Problem2AnswerType)) = [ + Example( + "R 6 (#70c710) +D 5 (#0dc571) +L 2 (#5713f0) +D 2 (#d2c081) +R 2 (#59c680) +D 2 (#411b91) +L 5 (#8ceee2) +U 2 (#caa173) +L 1 (#1b58a2) +U 2 (#caa171) +R 2 (#7807d2) +U 3 (#a77fa3) +L 2 (#015232) +U 2 (#7a21e3)", + "952408144115", + ), +] + +pub fn part1_test() { + part1_examples + |> should.not_equal([]) + use example <- list.map(part1_examples) + solve.part1(example.input) + |> should.equal(example.answer) +} + +pub fn part2_test() { + part2_examples + |> should.not_equal([]) + use example <- list.map(part2_examples) + solve.part2(example.input) + |> should.equal(example.answer) +} diff --git a/gleam/aoc2023/test/day19/day19_test.gleam b/gleam/aoc2023/test/day19/day19_test.gleam new file mode 100644 index 0000000..c911de5 --- /dev/null +++ b/gleam/aoc2023/test/day19/day19_test.gleam @@ -0,0 +1,80 @@ +import gleam/list +import showtime/tests/should +import adglent.{type Example, Example} +import day19/solve + +type Problem1AnswerType = + String + +type Problem2AnswerType = + String + +/// Add examples for part 1 here: +/// ```gleam +///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] +/// ``` +const part1_examples: List(Example(Problem1AnswerType)) = [ + Example( + "px{a<2006:qkq,m>2090:A,rfg} +pv{a>1716:R,A} +lnx{m>1548:A,A} +rfg{s<537:gd,x>2440:R,A} +qs{s>3448:A,lnx} +qkq{x<1416:A,crn} +crn{x>2662:A,R} +in{s<1351:px,qqz} +qqz{s>2770:qs,m<1801:hdj,R} +gd{a>3333:R,R} +hdj{m>838:A,pv} + +{x=787,m=2655,a=1222,s=2876} +{x=1679,m=44,a=2067,s=496} +{x=2036,m=264,a=79,s=2244} +{x=2461,m=1339,a=466,s=291} +{x=2127,m=1623,a=2188,s=1013}", + "19114", + ), +] + +/// Add examples for part 2 here: +/// ```gleam +///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] +/// ``` +const part2_examples: List(Example(Problem2AnswerType)) = [ + Example( + "px{a<2006:qkq,m>2090:A,rfg} +pv{a>1716:R,A} +lnx{m>1548:A,A} +rfg{s<537:gd,x>2440:R,A} +qs{s>3448:A,lnx} +qkq{x<1416:A,crn} +crn{x>2662:A,R} +in{s<1351:px,qqz} +qqz{s>2770:qs,m<1801:hdj,R} +gd{a>3333:R,R} +hdj{m>838:A,pv} + +{x=787,m=2655,a=1222,s=2876} +{x=1679,m=44,a=2067,s=496} +{x=2036,m=264,a=79,s=2244} +{x=2461,m=1339,a=466,s=291} +{x=2127,m=1623,a=2188,s=1013}", + "167409079868000", + ), +] + +pub fn part1_test() { + part1_examples + |> should.not_equal([]) + use example <- list.map(part1_examples) + solve.part1(example.input) + |> should.equal(example.answer) +} + +pub fn part2_test() { + part2_examples + |> should.not_equal([]) + use example <- list.map(part2_examples) + solve.part2(example.input) + |> should.equal(example.answer) +} diff --git a/gleam/aoc2023/test/day2/day2_test.gleam b/gleam/aoc2023/test/day2/day2_test.gleam new file mode 100644 index 0000000..28a65da --- /dev/null +++ b/gleam/aoc2023/test/day2/day2_test.gleam @@ -0,0 +1,57 @@ +import gleam/list +import showtime/tests/should +import adglent.{type Example, Example} +import day2/solve + +type Problem1AnswerType = + Int + +type Problem2AnswerType = + Int + +/// Add examples for part 1 here: +/// ```gleam +///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] +/// ``` +const part1_examples: List(Example(Problem1AnswerType)) = [ + Example( + "Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green +Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue +Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red +Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red +Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green", + 8, + ), +] + +/// Add examples for part 2 here: +/// ```gleam +///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] +/// ``` +const part2_examples: List(Example(Problem2AnswerType)) = [ + Example( + "Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green +Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue +Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red +Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red +Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green", + 2286, + ), + Example("Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green", 48), +] + +pub fn part1_test() { + part1_examples + |> should.not_equal([]) + use example <- list.map(part1_examples) + solve.part1(example.input) + |> should.equal(example.answer) +} + +pub fn part2_test() { + part2_examples + |> should.not_equal([]) + use example <- list.map(part2_examples) + solve.part2(example.input) + |> should.equal(example.answer) +} diff --git a/gleam/aoc2023/test/day20/day20_test.gleam b/gleam/aoc2023/test/day20/day20_test.gleam new file mode 100644 index 0000000..9b79b05 --- /dev/null +++ b/gleam/aoc2023/test/day20/day20_test.gleam @@ -0,0 +1,55 @@ +import gleam/list +import showtime/tests/should +import adglent.{type Example, Example} +import day20/solve + +type Problem1AnswerType = + String + +type Problem2AnswerType = + String + +/// Add examples for part 1 here: +/// ```gleam +///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] +/// ``` +const part1_examples: List(Example(Problem1AnswerType)) = [ + Example( + "broadcaster -> a, b, c +%a -> b +%b -> c +%c -> inv +&inv -> a", + "32000000", + ), + Example( + "broadcaster -> a +%a -> inv, con +&inv -> b +%b -> con +&con -> output +output -> ", + "11687500", + ), +] + +// const part2_examples: List(Example(Problem2AnswerType)) = [] + +/// Add examples for part 2 here: +/// ```gleam +///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] +/// ``` +pub fn part1_test() { + part1_examples + |> should.not_equal([]) + use example <- list.map(part1_examples) + solve.part1(example.input) + |> should.equal(example.answer) +} +// pub fn part2_test() { +// part2_examples +// |> should.not_equal([]) +// use example <- list.map(part2_examples) +// solve.part2(example.input) +// |> should.equal(example.answer) +// } diff --git a/gleam/aoc2023/test/day21/day21_test.gleam b/gleam/aoc2023/test/day21/day21_test.gleam new file mode 100644 index 0000000..5f46808 --- /dev/null +++ b/gleam/aoc2023/test/day21/day21_test.gleam @@ -0,0 +1,38 @@ +import gleam/list +import showtime/tests/should +import adglent.{type Example, Example} +import day21/solve + +type Problem1AnswerType = + String + +type Problem2AnswerType = + String + +/// Add examples for part 1 here: +/// ```gleam +///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] +/// ``` +const part1_examples: List(Example(Problem1AnswerType)) = [] + +/// Add examples for part 2 here: +/// ```gleam +///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] +/// ``` +const part2_examples: List(Example(Problem2AnswerType)) = [] + +pub fn part1_test() { + part1_examples + |> should.not_equal([]) + use example <- list.map(part1_examples) + solve.part1(example.input) + |> should.equal(example.answer) +} + +pub fn part2_test() { + part2_examples + |> should.not_equal([]) + use example <- list.map(part2_examples) + solve.part2(example.input) + |> should.equal(example.answer) +} diff --git a/gleam/aoc2023/test/day22/day22_test.gleam b/gleam/aoc2023/test/day22/day22_test.gleam new file mode 100644 index 0000000..3f8c0ca --- /dev/null +++ b/gleam/aoc2023/test/day22/day22_test.gleam @@ -0,0 +1,60 @@ +import gleam/list +import showtime/tests/should +import adglent.{type Example, Example} +import day22/solve + +type Problem1AnswerType = + Int + +type Problem2AnswerType = + Int + +/// Add examples for part 1 here: +/// ```gleam +///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] +/// ``` +const part1_examples: List(Example(Problem1AnswerType)) = [ + Example( + "1,0,1~1,2,1 +0,0,2~2,0,2 +0,2,3~2,2,3 +0,0,4~0,2,4 +2,0,5~2,2,5 +0,1,6~2,1,6 +1,1,8~1,1,9", + 5, + ), +] + +/// Add examples for part 2 here: +/// ```gleam +///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] +/// ``` +const part2_examples: List(Example(Problem2AnswerType)) = [ + Example( + "1,0,1~1,2,1 +0,0,2~2,0,2 +0,2,3~2,2,3 +0,0,4~0,2,4 +2,0,5~2,2,5 +0,1,6~2,1,6 +1,1,8~1,1,9", + 7, + ), +] + +pub fn part1_test() { + part1_examples + |> should.not_equal([]) + use example <- list.map(part1_examples) + solve.part1(example.input) + |> should.equal(example.answer) +} + +pub fn part2_test() { + part2_examples + |> should.not_equal([]) + use example <- list.map(part2_examples) + solve.part2(example.input) + |> should.equal(example.answer) +} diff --git a/gleam/aoc2023/test/day23/day23_test.gleam b/gleam/aoc2023/test/day23/day23_test.gleam new file mode 100644 index 0000000..206571c --- /dev/null +++ b/gleam/aoc2023/test/day23/day23_test.gleam @@ -0,0 +1,92 @@ +import gleam/list +import showtime/tests/should +import adglent.{type Example, Example} +import day23/solve + +type Problem1AnswerType = + Int + +type Problem2AnswerType = + Int + +/// Add examples for part 1 here: +/// ```gleam +///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] +/// ``` +const part1_examples: List(Example(Problem1AnswerType)) = [ + Example( + "#.##################### +#.......#########...### +#######.#########.#.### +###.....#.>.>.###.#.### +###v#####.#v#.###.#.### +###.>...#.#.#.....#...# +###v###.#.#.#########.# +###...#.#.#.......#...# +#####.#.#.#######.#.### +#.....#.#.#.......#...# +#.#####.#.#.#########v# +#.#...#...#...###...>.# +#.#.#v#######v###.###v# +#...#.>.#...>.>.#.###.# +#####v#.#.###v#.#.###.# +#.....#...#...#.#.#...# +#.#########.###.#.#.### +#...###...#...#...#.### +###.###.#.###v#####v### +#...#...#.#.>.>.#.>.### +#.###.###.#.###.#.#v### +#.....###...###...#...# +#####################.#", + 94, + ), +] + +/// Add examples for part 2 here: +/// ```gleam +///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] +/// ``` +const part2_examples: List(Example(Problem2AnswerType)) = [ + Example( + "#.##################### +#.......#########...### +#######.#########.#.### +###.....#.>.>.###.#.### +###v#####.#v#.###.#.### +###.>...#.#.#.....#...# +###v###.#.#.#########.# +###...#.#.#.......#...# +#####.#.#.#######.#.### +#.....#.#.#.......#...# +#.#####.#.#.#########v# +#.#...#...#...###...>.# +#.#.#v#######v###.###v# +#...#.>.#...>.>.#.###.# +#####v#.#.###v#.#.###.# +#.....#...#...#.#.#...# +#.#########.###.#.#.### +#...###...#...#...#.### +###.###.#.###v#####v### +#...#...#.#.>.>.#.>.### +#.###.###.#.###.#.#v### +#.....###...###...#...# +#####################.#", + 154, + ), +] + +pub fn part1_test() { + part1_examples + |> should.not_equal([]) + use example <- list.map(part1_examples) + solve.part1(example.input) + |> should.equal(example.answer) +} + +pub fn part2_test() { + part2_examples + |> should.not_equal([]) + use example <- list.map(part2_examples) + solve.part2(example.input) + |> should.equal(example.answer) +} diff --git a/gleam/aoc2023/test/day3/day3_test.gleam b/gleam/aoc2023/test/day3/day3_test.gleam new file mode 100644 index 0000000..30e17a9 --- /dev/null +++ b/gleam/aoc2023/test/day3/day3_test.gleam @@ -0,0 +1,66 @@ +import gleam/list +import showtime/tests/should +import adglent.{type Example, Example} +import day3/solve + +type Problem1AnswerType = + Int + +type Problem2AnswerType = + Int + +/// Add examples for part 1 here: +/// ```gleam +///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] +/// ``` +const part1_examples: List(Example(Problem1AnswerType)) = [ + Example( + "467..114.. +...*...... +..35..633. +......#... +617*...... +.....+.58. +..592..... +......755. +...$.*.... +.664.598..", + 4361, + ), +] + +/// Add examples for part 2 here: +/// ```gleam +///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] +/// ``` +const part2_examples: List(Example(Problem2AnswerType)) = [ + Example( + "467..114.. +...*...... +..35..633. +......#... +617*...... +.....+.58. +..592..... +......755. +...$.*.... +.664.598..", + 467_835, + ), +] + +pub fn part1_test() { + part1_examples + |> should.not_equal([]) + use example <- list.map(part1_examples) + solve.part1(example.input) + |> should.equal(example.answer) +} + +pub fn part2_test() { + part2_examples + |> should.not_equal([]) + use example <- list.map(part2_examples) + solve.part2(example.input) + |> should.equal(example.answer) +} diff --git a/gleam/aoc2023/test/day4/day4_test.gleam b/gleam/aoc2023/test/day4/day4_test.gleam new file mode 100644 index 0000000..324fe36 --- /dev/null +++ b/gleam/aoc2023/test/day4/day4_test.gleam @@ -0,0 +1,58 @@ +import gleam/list +import showtime/tests/should +import adglent.{type Example, Example} +import day4/solve + +type Problem1AnswerType = + Int + +type Problem2AnswerType = + Int + +/// Add examples for part 1 here: +/// ```gleam +///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] +/// ``` +const part1_examples: List(Example(Problem1AnswerType)) = [ + Example( + "Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53 +Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19 +Card 3: 1 21 53 59 44 | 69 82 63 72 16 21 14 1 +Card 4: 41 92 73 84 69 | 59 84 76 51 58 5 54 83 +Card 5: 87 83 26 28 32 | 88 30 70 12 93 22 82 36 +Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11", + 13, + ), +] + +/// Add examples for part 2 here: +/// ```gleam +///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] +/// ``` +const part2_examples: List(Example(Problem2AnswerType)) = [ + Example( + "Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53 +Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19 +Card 3: 1 21 53 59 44 | 69 82 63 72 16 21 14 1 +Card 4: 41 92 73 84 69 | 59 84 76 51 58 5 54 83 +Card 5: 87 83 26 28 32 | 88 30 70 12 93 22 82 36 +Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11", + 30, + ), +] + +pub fn part1_test() { + part1_examples + |> should.not_equal([]) + use example <- list.map(part1_examples) + solve.part1(example.input) + |> should.equal(example.answer) +} + +pub fn part2_test() { + part2_examples + |> should.not_equal([]) + use example <- list.map(part2_examples) + solve.part2(example.input) + |> should.equal(example.answer) +} diff --git a/gleam/aoc2023/test/day5/day5_test.gleam b/gleam/aoc2023/test/day5/day5_test.gleam new file mode 100644 index 0000000..86a8692 --- /dev/null +++ b/gleam/aoc2023/test/day5/day5_test.gleam @@ -0,0 +1,112 @@ +import gleam/list +import showtime/tests/should +import adglent.{type Example, Example} +import day5/solve + +type Problem1AnswerType = + String + +type Problem2AnswerType = + String + +/// Add examples for part 1 here: +/// ```gleam +///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] +/// ``` +const part1_examples: List(Example(Problem1AnswerType)) = [ + Example( + "seeds: 79 14 55 13 + +seed-to-soil map: +50 98 2 +52 50 48 + +soil-to-fertilizer map: +0 15 37 +37 52 2 +39 0 15 + +fertilizer-to-water map: +49 53 8 +0 11 42 +42 0 7 +57 7 4 + +water-to-light map: +88 18 7 +18 25 70 + +light-to-temperature map: +45 77 23 +81 45 19 +68 64 13 + +temperature-to-humidity map: +0 69 1 +1 0 69 + +humidity-to-location map: +60 56 37 +56 93 4", + "35", + ), +] + +/// Add examples for part 2 here: +/// ```gleam +///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] +/// ``` +const part2_examples: List(Example(Problem2AnswerType)) = [ + Example( + "seeds: 79 14 55 13 + +seed-to-soil map: +50 98 2 +52 50 48 + +soil-to-fertilizer map: +0 15 37 +37 52 2 +39 0 15 + +fertilizer-to-water map: +49 53 8 +0 11 42 +42 0 7 +57 7 4 + +water-to-light map: +88 18 7 +18 25 70 + +light-to-temperature map: +45 77 23 +81 45 19 +68 64 13 + +temperature-to-humidity map: +0 69 1 +1 0 69 + +humidity-to-location map: +60 56 37 +56 93 4", + "46", + ), +] + +pub fn part1_test() { + part1_examples + |> should.not_equal([]) + use example <- list.map(part1_examples) + solve.part1(example.input) + |> should.equal(example.answer) +} + +pub fn part2_test() { + part2_examples + |> should.not_equal([]) + use example <- list.map(part2_examples) + solve.part2(example.input) + |> should.equal(example.answer) +} diff --git a/gleam/aoc2023/test/day6/day6_test.gleam b/gleam/aoc2023/test/day6/day6_test.gleam new file mode 100644 index 0000000..c551993 --- /dev/null +++ b/gleam/aoc2023/test/day6/day6_test.gleam @@ -0,0 +1,50 @@ +import gleam/list +import showtime/tests/should +import adglent.{type Example, Example} +import day6/solve + +type Problem1AnswerType = + String + +type Problem2AnswerType = + String + +/// Add examples for part 1 here: +/// ```gleam +///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] +/// ``` +const part1_examples: List(Example(Problem1AnswerType)) = [ + Example( + "Time: 7 15 30 +Distance: 9 40 200", + "288", + ), +] + +/// Add examples for part 2 here: +/// ```gleam +///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] +/// ``` +const part2_examples: List(Example(Problem2AnswerType)) = [ + Example( + "Time: 7 15 30 +Distance: 9 40 200", + "71503", + ), +] + +pub fn part1_test() { + part1_examples + |> should.not_equal([]) + use example <- list.map(part1_examples) + solve.part1(example.input) + |> should.equal(example.answer) +} + +pub fn part2_test() { + part2_examples + |> should.not_equal([]) + use example <- list.map(part2_examples) + solve.part2(example.input) + |> should.equal(example.answer) +} diff --git a/gleam/aoc2023/test/day7/day7_test.gleam b/gleam/aoc2023/test/day7/day7_test.gleam new file mode 100644 index 0000000..f7f8454 --- /dev/null +++ b/gleam/aoc2023/test/day7/day7_test.gleam @@ -0,0 +1,56 @@ +import gleam/list +import showtime/tests/should +import adglent.{type Example, Example} +import day7/solve + +type Problem1AnswerType = + String + +type Problem2AnswerType = + String + +/// Add examples for part 1 here: +/// ```gleam +///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] +/// ``` +const part1_examples: List(Example(Problem1AnswerType)) = [ + Example( + "32T3K 765 +T55J5 684 +KK677 28 +KTJJT 220 +QQQJA 483", + "6440", + ), +] + +/// Add examples for part 2 here: +/// ```gleam +///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] +/// ``` +const part2_examples: List(Example(Problem2AnswerType)) = [ + Example( + "32T3K 765 +T55J5 684 +KK677 28 +KTJJT 220 +QQQJA 483", + "5905", + ), +] + +pub fn part1_test() { + part1_examples + |> should.not_equal([]) + use example <- list.map(part1_examples) + solve.part1(example.input) + |> should.equal(example.answer) +} + +pub fn part2_test() { + part2_examples + |> should.not_equal([]) + use example <- list.map(part2_examples) + solve.part2(example.input) + |> should.equal(example.answer) +} diff --git a/gleam/aoc2023/test/day8/day8_test.gleam b/gleam/aoc2023/test/day8/day8_test.gleam new file mode 100644 index 0000000..2cd499a --- /dev/null +++ b/gleam/aoc2023/test/day8/day8_test.gleam @@ -0,0 +1,61 @@ +import gleam/list +import showtime/tests/should +import adglent.{type Example, Example} +import day8/solve + +type Problem1AnswerType = + Int + +type Problem2AnswerType = + Int + +/// Add examples for part 1 here: +/// ```gleam +///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] +/// ``` +const part1_examples: List(Example(Problem1AnswerType)) = [ + Example( + "LLR + +AAA = (BBB, BBB) +BBB = (AAA, ZZZ) +ZZZ = (ZZZ, ZZZ)", + 6, + ), +] + +/// Add examples for part 2 here: +/// ```gleam +///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] +/// ``` +const part2_examples: List(Example(Problem2AnswerType)) = [ + Example( + "LR + +11A = (11B, XXX) +11B = (XXX, 11Z) +11Z = (11B, XXX) +22A = (22B, XXX) +22B = (22C, 22C) +22C = (22Z, 22Z) +22Z = (22B, 22B) +XXX = (XXX, XXX)", + 6, + ), +] + +pub fn part1_test() { + part1_examples + |> should.not_equal([]) + use example <- list.map(part1_examples) + solve.part1(example.input) + |> should.equal(example.answer) +} + +pub fn part2_test() { + part2_examples + |> should.not_equal([]) + use example <- list.map(part2_examples) + solve.part2(example.input) + |> should.equal(example.answer) +} diff --git a/gleam/aoc2023/test/day9/day9_test.gleam b/gleam/aoc2023/test/day9/day9_test.gleam new file mode 100644 index 0000000..84fd3ba --- /dev/null +++ b/gleam/aoc2023/test/day9/day9_test.gleam @@ -0,0 +1,52 @@ +import gleam/list +import showtime/tests/should +import adglent.{type Example, Example} +import day9/solve + +type Problem1AnswerType = + String + +type Problem2AnswerType = + String + +/// Add examples for part 1 here: +/// ```gleam +///const part1_examples: List(Example(Problem1AnswerType)) = [Example("some input", "")] +/// ``` +const part1_examples: List(Example(Problem1AnswerType)) = [ + Example( + "0 3 6 9 12 15 +1 3 6 10 15 21 +10 13 16 21 30 45", + "114", + ), +] + +/// Add examples for part 2 here: +/// ```gleam +///const part2_examples: List(Example(Problem2AnswerType)) = [Example("some input", "")] +/// ``` +const part2_examples: List(Example(Problem2AnswerType)) = [ + Example( + "0 3 6 9 12 15 +1 3 6 10 15 21 +10 13 16 21 30 45", + "2", + ), +] + +pub fn part1_test() { + part1_examples + |> should.not_equal([]) + use example <- list.map(part1_examples) + solve.part1(example.input) + |> should.equal(example.answer) +} + +pub fn part2_test() { + part2_examples + |> should.not_equal([]) + use example <- list.map(part2_examples) + solve.part2(example.input) + |> should.equal(example.answer) +} diff --git a/gleam/codingquest2024/.github/workflows/test.yml b/gleam/codingquest2024/.github/workflows/test.yml new file mode 100644 index 0000000..916edea --- /dev/null +++ b/gleam/codingquest2024/.github/workflows/test.yml @@ -0,0 +1,23 @@ +name: test + +on: + push: + branches: + - master + - main + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: erlef/setup-beam@v1 + with: + otp-version: "26.0.2" + gleam-version: "1.0.0" + rebar3-version: "3" + # elixir-version: "1.15.4" + - run: gleam deps download + - run: gleam test + - run: gleam format --check src test diff --git a/gleam/codingquest2024/.gitignore b/gleam/codingquest2024/.gitignore new file mode 100644 index 0000000..6fdd6db --- /dev/null +++ b/gleam/codingquest2024/.gitignore @@ -0,0 +1,5 @@ +*.beam +*.ez +/build +erl_crash.dump +data.txt diff --git a/gleam/codingquest2024/README.md b/gleam/codingquest2024/README.md new file mode 100644 index 0000000..8412b48 --- /dev/null +++ b/gleam/codingquest2024/README.md @@ -0,0 +1,25 @@ +# codingquest2024 + +[![Package Version](https://img.shields.io/hexpm/v/codingquest2024)](https://hex.pm/packages/codingquest2024) +[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/codingquest2024/) + +```sh +gleam add codingquest2024 +``` +```gleam +import codingquest2024 + +pub fn main() { + // TODO: An example of the project in use +} +``` + +Further documentation can be found at . + +## Development + +```sh +gleam run # Run the project +gleam test # Run the tests +gleam shell # Run an Erlang shell +``` diff --git a/gleam/codingquest2024/gleam.toml b/gleam/codingquest2024/gleam.toml new file mode 100644 index 0000000..f39baa5 --- /dev/null +++ b/gleam/codingquest2024/gleam.toml @@ -0,0 +1,22 @@ +name = "codingquest2024" +version = "1.0.0" + +# Fill out these fields if you intend to generate HTML documentation or publish +# your project to the Hex package manager. +# +# description = "" +# licences = ["Apache-2.0"] +# repository = { type = "github", user = "username", repo = "project" } +# links = [{ title = "Website", href = "https://gleam.run" }] +# +# For a full reference of all the available options, you can have a look at +# https://gleam.run/writing-gleam/gleam-toml/. + +[dependencies] +gleam_stdlib = "~> 0.34 or ~> 1.0" +simplifile = "~> 1.5" +gleam_otp = "~> 0.10" +gleam_erlang = "~> 0.24" + +[dev-dependencies] +gleeunit = "~> 1.0" diff --git a/gleam/codingquest2024/manifest.toml b/gleam/codingquest2024/manifest.toml new file mode 100644 index 0000000..55b1f61 --- /dev/null +++ b/gleam/codingquest2024/manifest.toml @@ -0,0 +1,17 @@ +# This file was generated by Gleam +# You typically do not need to edit this file + +packages = [ + { name = "gleam_erlang", version = "0.24.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "26BDB52E61889F56A291CB34167315780EE4AA20961917314446542C90D1C1A0" }, + { name = "gleam_otp", version = "0.10.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "0B04FE915ACECE539B317F9652CAADBBC0F000184D586AAAF2D94C100945D72B" }, + { name = "gleam_stdlib", version = "0.36.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "C0D14D807FEC6F8A08A7C9EF8DFDE6AE5C10E40E21325B2B29365965D82EB3D4" }, + { name = "gleeunit", version = "1.0.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "D364C87AFEB26BDB4FB8A5ABDE67D635DC9FA52D6AB68416044C35B096C6882D" }, + { name = "simplifile", version = "1.5.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "EB9AA8E65E5C1E3E0FDCFC81BC363FD433CB122D7D062750FFDF24DE4AC40116" }, +] + +[requirements] +gleam_erlang = { version = "~> 0.24"} +gleam_otp = { version = "~> 0.10" } +gleam_stdlib = { version = "~> 0.34 or ~> 1.0" } +gleeunit = { version = "~> 1.0" } +simplifile = { version = "~> 1.5" } diff --git a/gleam/codingquest2024/src/codingquest2024.gleam b/gleam/codingquest2024/src/codingquest2024.gleam new file mode 100644 index 0000000..5af4a2a --- /dev/null +++ b/gleam/codingquest2024/src/codingquest2024.gleam @@ -0,0 +1,5 @@ +import gleam/io + +pub fn main() { + io.println("Hello from codingquest2024!") +} diff --git a/gleam/codingquest2024/src/day1/solution.gleam b/gleam/codingquest2024/src/day1/solution.gleam new file mode 100644 index 0000000..abef2eb --- /dev/null +++ b/gleam/codingquest2024/src/day1/solution.gleam @@ -0,0 +1,55 @@ +import gleam/dict.{type Dict} +import gleam/int +import gleam/io +import gleam/list +import gleam/option.{None, Some} +import gleam/regex +import gleam/result +import gleam/string +import simplifile + +pub type Adjustment { + Adjustment(spaceliner: String, value: Int) +} + +pub fn main() { + let assert Ok(data) = simplifile.read(from: "./src/day1/data.txt") + + let raw_adjustments = string.split(data, "\n") + + raw_adjustments + |> collate_adjustments + |> dict.values + |> list.reduce(with: int.min) + |> result.unwrap(0) + |> int.to_string() + |> io.println() +} + +fn parse_line(line: String) -> Adjustment { + let assert Ok(re) = regex.from_string("[\\s:]+") + + let assert [spaceliner, kind, raw_value] = + regex.split(with: re, content: line) + + let assert Ok(value) = int.parse(raw_value) + Adjustment(spaceliner, classify_adjustment(kind) * value) +} + +fn classify_adjustment(name: String) -> Int { + case name { + "Seat" | "Meals" | "Luggage" | "Fee" | "Tax" -> 1 + "Discount" | "Rebate" -> -1 + _ -> panic as "unknown adjustment kind" + } +} + +fn collate_adjustments(raw_adjustments: List(String)) -> Dict(String, Int) { + use acc, this <- list.fold(over: raw_adjustments, from: dict.new()) + let adjustment = parse_line(this) + use maybe_v <- dict.update(in: acc, update: adjustment.spaceliner) + case maybe_v { + None -> adjustment.value + Some(v) -> v + adjustment.value + } +} diff --git a/gleam/codingquest2024/src/day2/solution.gleam b/gleam/codingquest2024/src/day2/solution.gleam new file mode 100644 index 0000000..59f8d29 --- /dev/null +++ b/gleam/codingquest2024/src/day2/solution.gleam @@ -0,0 +1,66 @@ +import gleam/bit_array +import gleam/list +import gleam/int +import gleam/io +import gleam/string +import simplifile + +pub type Location { + Internal + Passenger + Other +} + +pub type Packet { + Packet(length: Int, endpoint: Location) +} + +pub fn main() { + let assert Ok(data) = simplifile.read(from: "./src/day2/data.txt") + + let #(internal_packets, passenger_packets) = + data + |> string.split("\n") + |> list.map(parse_packet) + |> list.partition(fn(p) { p.endpoint == Internal }) + + { + int.to_string(sum_packet_lengths(internal_packets)) + <> "/" + <> int.to_string(sum_packet_lengths(passenger_packets)) + } + |> io.println() +} + +fn parse_packet(raw_packet: String) { + let assert Ok(<< + _:bytes-size(2), + length:int-size(16), + _:bytes-size(8), + source:bytes-size(4), + destination:bytes-size(4), + >>) = bit_array.base16_decode(raw_packet) + + Packet(length, set_endpoint(source, destination)) +} + +fn set_endpoint(source: BitArray, dest: BitArray) -> Location { + case identify_location(source), identify_location(dest) { + Other, Internal | Internal, Other -> Internal + Other, Passenger | Passenger, Other -> Passenger + _, _ -> Other + } +} + +fn identify_location(ip: BitArray) -> Location { + case ip { + <<192, 168, _, _>> -> Internal + <<10, 0, _, _>> -> Passenger + _ -> Other + } +} + +fn sum_packet_lengths(packets: List(Packet)) -> Int { + use acc, p <- list.fold(packets, 0) + acc + p.length +} diff --git a/gleam/codingquest2024/src/day3/solution.gleam b/gleam/codingquest2024/src/day3/solution.gleam new file mode 100644 index 0000000..6314060 --- /dev/null +++ b/gleam/codingquest2024/src/day3/solution.gleam @@ -0,0 +1,50 @@ +import gleam/bit_array +import gleam/int +import gleam/io +import gleam/string +import gleam/string_builder.{type StringBuilder} +import simplifile + +pub fn main() { + let assert Ok(data) = simplifile.read(from: "./src/day3/data.txt") + + data + |> string.split(" ") + |> build_flat_image(" ", string_builder.new()) + |> bit_array.from_string() + |> print_next_slice() +} + +fn build_flat_image( + nums: List(String), + pixel: String, + acc: StringBuilder, +) -> String { + case nums { + [] -> string_builder.to_string(acc) + [h, ..t] -> { + let assert Ok(n) = int.parse(h) + let pixels = string.repeat(pixel, n) + build_flat_image(t, next_pixel(pixel), string_builder.append(acc, pixels)) + } + } +} + +fn print_next_slice(str: BitArray) -> Nil { + case str { + <> -> { + let assert Ok(out) = bit_array.to_string(slice) + io.println(out) + print_next_slice(rest) + } + _ -> Nil + } +} + +fn next_pixel(p: String) -> String { + case p { + " " -> "#" + "#" -> " " + _ -> panic + } +} diff --git a/gleam/codingquest2024/src/day4/solution.gleam b/gleam/codingquest2024/src/day4/solution.gleam new file mode 100644 index 0000000..a03b2be --- /dev/null +++ b/gleam/codingquest2024/src/day4/solution.gleam @@ -0,0 +1,49 @@ +import gleam/float +import gleam/io +import gleam/list +import gleam/regex +import gleam/result +import gleam/string +import simplifile + +pub type Star { + Star(name: String, x: Float, y: Float, z: Float) +} + +pub fn main() { + let assert Ok(data) = simplifile.read(from: "./src/day4/data.txt") + + let assert [_, ..stars] = string.split(data, "\n") + + stars + |> list.map(parse_star_record) + |> list.combination_pairs() + |> list.map(star_distance) + |> list.reduce(float.min) + |> io.debug +} + +fn parse_star_record(str: String) -> Star { + let assert Ok(re) = regex.from_string("\\s{2,}") + + let assert [name, ..coords] = regex.split(with: re, content: str) + let assert [_, x, y, z] = + coords + |> list.map(float.parse) + |> result.values + + Star(name, x, y, z) +} + +fn star_distance(stars: #(Star, Star)) -> Float { + let #(star1, star2) = stars + let assert Ok(dist) = + float.square_root( + sq(star1.x -. star2.x) +. sq(star1.y -. star2.y) +. sq(star1.z -. star2.z), + ) + dist +} + +fn sq(x: Float) -> Float { + x *. x +} diff --git a/gleam/codingquest2024/src/day5/solution.gleam b/gleam/codingquest2024/src/day5/solution.gleam new file mode 100644 index 0000000..6c10693 --- /dev/null +++ b/gleam/codingquest2024/src/day5/solution.gleam @@ -0,0 +1,67 @@ +import gleam/dict.{type Dict} +import gleam/io +import gleam/int +import gleam/list +import gleam/regex +import gleam/result +import gleam/string +import simplifile + +type Atlas = + Dict(String, Dict(String, Int)) + +pub fn main() { + let assert Ok(data) = simplifile.read(from: "./src/day5/data.txt") + + let assert [table, routes] = string.split(data, "\n\n") + let assert [header, ..rows] = string.split(table, "\n") + + let dests = + header + |> string.trim() + |> split_on_many_spaces + + let atlas = build_atlas(rows, dests) + + routes + |> string.split("\n") + |> list.fold(0, fn(acc, route) { acc + find_total_distance(route, atlas) }) + |> io.debug +} + +fn split_on_many_spaces(str) { + let assert Ok(re_spaces) = regex.from_string("\\s+") + regex.split(re_spaces, str) +} + +fn build_atlas(rows, dests) { + rows + |> list.map(split_on_many_spaces) + |> list.fold(dict.new(), fn(acc, row) { + let assert [from, ..raw_dists] = row + let assert Ok(dists) = list.try_map(raw_dists, int.parse) + let to_dict = + dests + |> list.zip(dists) + |> dict.from_list() + + dict.insert(acc, from, to_dict) + }) +} + +fn dist_between(leg: #(String, String), dict: Atlas) -> Int { + let assert Ok(dist) = + dict + |> dict.get(leg.0) + |> result.try(dict.get(_, leg.1)) + + dist +} + +fn find_total_distance(row: String, atlas: Atlas) { + let assert [_, route] = string.split(row, ": ") + + string.split(route, " -> ") + |> list.window_by_2 + |> list.fold(0, fn(acc, leg) { acc + dist_between(leg, atlas) }) +} diff --git a/gleam/codingquest2024/src/day6/solution.gleam b/gleam/codingquest2024/src/day6/solution.gleam new file mode 100644 index 0000000..8a0319c --- /dev/null +++ b/gleam/codingquest2024/src/day6/solution.gleam @@ -0,0 +1,86 @@ +import gleam/dict +import gleam/io +import gleam/list +import gleam/regex +import gleam/result +import gleam/string +import simplifile + +pub opaque type Location { + Location(row: Int, col: Int) +} + +pub fn main() { + let assert Ok(data) = simplifile.read(from: "./src/day6/data.txt") + let assert ["Cipher key: " <> key, "Message: " <> raw_message] = + string.split(data, "\n") + + let letter_to_location = make_cipher_grid(key) + let location_to_letter = + letter_to_location + |> dict.fold(dict.new(), fn(acc, k, v) { dict.insert(acc, v, k) }) + + raw_message + |> string.split(" ") + |> list.map(fn(s) { + s + |> string.to_graphemes + |> list.sized_chunk(2) + }) + |> list.map(fn(s) { + s + |> decode_word(letter_to_location, location_to_letter) + |> string.concat() + }) + |> string.join(" ") + |> io.println() +} + +fn make_cipher_grid(key) { + let assert Ok(in_key) = regex.from_string("[" <> key <> "]") + let grid = + regex.split(in_key, "abcdefghiklmnopqrstuvwxyz") + |> string.concat + |> string.append(key, _) + |> string.to_graphemes + |> list.sized_chunk(5) + + list.index_map(grid, fn(row, r) { + list.index_map(row, fn(cell, c) { #(cell, Location(r, c)) }) + }) + |> list.flatten + |> dict.from_list +} + +fn decode_word(word: List(List(String)), to_loc, to_letter) { + case word { + [] -> [] + [[a, b], ..rest] -> [ + transform_pair(a, b, to_loc, to_letter), + ..decode_word(rest, to_loc, to_letter) + ] + _ -> panic as "bad playfair format" + } +} + +fn transform_pair(a, b, to_loc, to_letter) { + let assert Ok(Location(r_a, c_a)) = dict.get(to_loc, a) + let assert Ok(Location(r_b, c_b)) = dict.get(to_loc, b) + + case r_a == r_b, c_a == c_b { + True, _ -> [ + dict.get(to_letter, Location(r_a, { c_a + 4 } % 5)), + dict.get(to_letter, Location(r_b, { c_b + 4 } % 5)), + ] + _, True -> [ + dict.get(to_letter, Location({ r_a + 4 } % 5, c_a)), + dict.get(to_letter, Location({ r_b + 4 } % 5, c_b)), + ] + _, _ -> [ + dict.get(to_letter, Location(r_a, c_b)), + dict.get(to_letter, Location(r_b, c_a)), + ] + } + |> result.values + |> string.concat +} diff --git a/gleam/codingquest2024/src/day7/solution.gleam b/gleam/codingquest2024/src/day7/solution.gleam new file mode 100644 index 0000000..2ca7cbf --- /dev/null +++ b/gleam/codingquest2024/src/day7/solution.gleam @@ -0,0 +1,73 @@ +import gleam/io +import gleam/string +import gleam/set.{type Set} +import gleam/int +import gleam/regex.{Match} +import gleam/option.{Some} +import simplifile + +pub opaque type Item { + File(name: String, size: Int) + Directory(name: String) +} + +pub fn main() { + let assert Ok(data) = simplifile.read(from: "./src/day7/data.txt") + let lines = string.split(data, "\n") + + mark_for_deletion(lines, 0, "", set.new()) + |> io.debug() +} + +fn mark_for_deletion( + lines: List(String), + deleted: Int, + current_folder: String, + deleted_folders: Set(String), +) { + case lines { + [] -> deleted + ["Folder: " <> folder, ..rest] -> + mark_for_deletion(rest, deleted, folder, deleted_folders) + [file, ..rest] -> { + case + string.contains(file, "temporary") + || string.contains(file, "delete") + || set.contains(deleted_folders, current_folder) + { + True -> + case string.contains(file, "[FOLDER") { + True -> { + file + |> get_folder_number() + |> set.insert(deleted_folders, _) + |> mark_for_deletion(rest, deleted, current_folder, _) + } + False -> { + file + |> get_file_size() + |> int.add(deleted, _) + |> mark_for_deletion(rest, _, current_folder, deleted_folders) + } + } + False -> + mark_for_deletion(rest, deleted, current_folder, deleted_folders) + } + } + } +} + +fn get_folder_number(file) { + let assert Ok(re) = regex.from_string("\\[FOLDER ([0-9]+)\\]") + + let assert [Match(submatches: [Some(n)], ..)] = regex.scan(re, file) + n +} + +fn get_file_size(file) { + let assert Ok(re) = regex.from_string("- .+ ([0-9]+)$") + + let assert [Match(submatches: [Some(n)], ..)] = regex.scan(re, file) + let assert Ok(n) = int.parse(n) + n +} diff --git a/gleam/codingquest2024/src/day8/solution.gleam b/gleam/codingquest2024/src/day8/solution.gleam new file mode 100644 index 0000000..cb4d907 --- /dev/null +++ b/gleam/codingquest2024/src/day8/solution.gleam @@ -0,0 +1,26 @@ +import gleam/io +import gleam/int +import gleam/list +import utilities/memo + +const options = [40, 12, 2, 1] + +const distance = 856 + +pub fn main() { + use cache <- memo.create() + solve(distance, cache) + |> io.debug +} + +fn solve(target, cache) { + use <- memo.memoize(cache, target) + case target { + 0 -> 1 + _ -> + options + |> list.filter(fn(n) { n <= target }) + |> list.map(fn(n) { solve(target - n, cache) }) + |> int.sum + } +} diff --git a/gleam/codingquest2024/src/day9/input.txt b/gleam/codingquest2024/src/day9/input.txt new file mode 100644 index 0000000..1d30914 --- /dev/null +++ b/gleam/codingquest2024/src/day9/input.txt @@ -0,0 +1,203 @@ +##################################################################################################### +......#.....#.#.......#.....#.....#.....#.#.................#...............#.............#.#.......# +#.###.#.#.#.#.#.#####.#.#.#.#.###.###.#.#.#.#######.#######.#########.#####.#.#####.#####.#.#.###.#.# +#.#.#...#.#.#.#.#...#...#.#...#.#.....#.#.#...#...#.#...#.....#.....#.#...#.#.#...#.#...#.#...#...#.# +#.#.#####.#.#.#.#.#.#####.#####.#######.#.###.#.#.#.#.#.#.###.#.###.#.#.#.#.#.#.#.#.#.#.#.#.###.##### +#...#$..#.$...#$#.#.$.#.#$..#.$....$..#.$....$#.#.$.#.#$..#.$.#.#$..#.$.#..$..#.$...#$#.#.$.#.#$#...# +###.#.#.#.#####.#.###.#.###.#.#######.#.###.###.#####.#####.###.#.###.###.###.#.#####.###.#.#.#.#.#.# +#.#...#.#.......#...#...#...#.......#.#...#...#...#...#...#.....#.#.....#.#...#.#.....#...#...#.#.#.# +#.#####.#########.#.#####.#.#########.###.#######.#.#.#.#.#######.###.#.###.###.#.#.#.#.#####.#.#.#.# +#.....#.#.#.......#...#...#.#...#...#...#...#.....#.#.#.#.#.....#...#.#...#.#.#.#.#.#.#.....#.#...#.# +#.###$#.#.$.###$####$.#.#$###.$.#.#$#.#.$##.#$####$.#.#$#.#.$.#.#$#.##$##.#$#.#.$##.#$####$.#.#$###.# +#...#...#.......#...#.#.......#.#.#.#.#...#.#.#.....#...#...#.#...#.#...#.....#.#...#.....#.#...#.#.# +###.#####.#####.#.#.#.#########.#.#.#.#.#.#.#.#########.#####.#####.#.#.#######.#.#.#.#.###.###.#.#.# +#.#.#.....#...#.#.#...#...#...#.#.#.#.#.#.#.#.#...#...#...#.#...#...#.#.......#.#.#.#.#...#...#...#.# +#.#.#.#####.#.###.###.#.#.#.#.#.#.#.###.###.#.#.#.#.#.###.#.###.#.###.#######.#.#.#.#.###.###.#####.# +#.#.#$#...$.#..$#.#.$.#.#$#.#.$.#.#$#...$...#$..#.$.#..$#.#.$.#..$#...$.#.#$..#.$.#.#$#.#.$...#$....# +#.#.#.#.#######.#.#.###.#.#.#####.#.#.#.#.#####.#######.#.#.#.#########.#.#.###.#.#.#.#.#.#.###.###.# +#.#.#.#...#...#.#.#.....#.#.#.....#.#.#.#.....#.#.#...#.#...#...........#.#.....#.#.#...#.#...#.#...# +#.#.#####.#.#.#.#.#########.#.#####.#.#.#####.#.#.#.#.#.#################.#######.#.###.#.###.#.###.# +#.#.#.....#.#.....#.......#...#.....#.#.....#.#.#...#.#...#...#...#...#.#.........#.#...#...#.#...#.# +#.#.#$####$.###$###.$####$####$.###$#.##$####$#.##$##.#$#.#.$.#.#$#.#.$.#.#$####$.###$####$.#.#$#.### +#.#.#.#...#.#.......#.#...#...#...#...........#.....#.#...#.#...#...#.#.......#.#.....#...#...#.#...# +#.#.#.###.#.#.#######.#.###.#.###.#######.#########.#.#.###.#########.#######.#.#######.#.#####.###.# +#.#.#...#...#.....#...#.....#...#.......#.#...#.......#.#...#.......#...#...#...#.......#...#.....#.# +#.#.#.#.#.#######.#.###########.###.###.#.#.#.#.#######.#.###.#.#######.#.#.###.#####.#####.#####.#.# +#...#$#.#.$...#$..#.$.#..$#...$...#$#.#.$...#$#...$...#$#...$.#..$....$.#.#$#...$...#$#...$...#$#.#.# +#.#####.###.#.#.###.#.#.#.#.#.###.#.#.#.#####.#####.#.#.###.#########.#.#.#.###.#.#.#.#.#####.#.#.#.# +#.#.....#...#...#...#...#...#...#.#...#.#...#.......#.#...#...#...#...#...#...#...#...#.......#...#.# +#.#.#.#.#.#######.#########.###.#.#####.#.###########.#.#.###.#.#.#.#########.#########.#######.###.# +#.#.#.#.#.......#.#...#...#...#.#.......#...........#.#.#...#.#.#...#.......#...#...#...#.........#.# +#.###$#.##$####$#.#.$.#.#$####$.###$####$.###$###.$##.#$###.$.#.#$###.$####$###.$.###$###.$####$###.# +#.....#.#.....#.#...#.#.#...#.......#...#.......#...#...#.#.#...#.#...#...#.....#...#.#.....#.......# +#######.###.#.#.#######.###.#.#####.#.#.###.#####.#.#####.#.###.###.###.#.#########.#.#.#####.#####.# +#.#.....#...#.#.#.......#.#.#...#.....#.#...#...#.#.#.....#.#...#...#...#...........#.#.#...#.#.#...# +#.#.#####.###.#.#.#######.#.###.#.#####.#####.#.###.#.#.###.#.###.###############.###.#.#.#.#.#.#.### +#.#.#$....$...#$#...$....$#.#.$.#.#$..#.$...#$#...$.#.#$..#.$.#.#$#...$....$....$....$#.#.$...#$..#.# +#.#.#.###.#.###.###.#######.#.#####.#.#####.#.#######.###.#.#.#.#.#.###########.#.#######.#####.###.# +#...#.#.#.#...#.....#.......#.......#.#.......#.....#...#...#.#.#.#.......#.#...#.#.....#...#.......# +#.###.#.#.###########.#####.#####.###.#####.###.###.###.#####.#.#.#######.#.#.###.#.###.#.#.#######.# +#.#.#.#.#.#...#.....#...#...#...#...#.....#.#...#.#...#.#...#.#.#.......#...#.#.....#...#.#.#.....#.# +#.#.#$#.#.$.#.#$###.$##.#$#.#.$.###$###.$.#.#$###.$.###$#.#.$.#.#$###.$####$#.##$####$###.$.#.#$#.#.# +#.#.....#.#.#.#.#...#.#...#.#.#...#...#.#.#.#.....#.#.....#.#...#...#.......#.#...#.#...#.#.#.#...#.# +#.#######.#.#.#.#.#.#.###.###.###.#.#.###.#.#####.#.#.#####.###.#.#.###.#####.#.#.#.###.###.#.#.###.# +#...#.....#.#...#.#...#.#.....#.#...#...#.#.#.....#...#...#...#.#.#...#.#...#.#.#.#...#...#...#.#...# +###.#.#####.#####.###.#.#######.#######.#.###.#########.#.#####.#.###.#.#.#.#.#.#.#.#.###.#.###.#.### +#.#.#$....$...#$..#.$.#..$..#.$....$..#.$.#..$#...$.#.#$#.#.$...#$..#.$.#.#$#...$.#.#$..#.$.#..$#.#.# +#.#.#.###.###.#.###.#.###.#.#.###.###.#.#.#.###.###.#.#.#.#.#######.#.###.#.#####.#.#####.#.#.###.#.# +#.#.#...#...#.#.#...#.....#.#.#...#...#.#...#...#...#.#.#...#.....#.#.....#.......#.......#.#.#.....# +#.#.#####.#.#.#.#.#########.#.#.#####.#.###.#.#####.#.#.#####.#####.###############.#########.#####.# +#.#.....#.#.#.#.#.......#...#.#.....#.#...#...#...#...#...#.....#...#.....................#...#...#.# +#.###$#.#.$##.#$###.$.###$###.$####$#.##$.###$#.#.$##.#$#.#.$##.#$###.$####$###.$####$###.$.#.#$#.#.# +#.....#.#.....#...#.#.#...#.#.#...#.#...#.......#...#...#.#.#...#.#.....#.....#...#.....#.#.#.#.#.#.# +#.#.###.#####.###.#.#.#.###.#.#.#.#.###.###########.###.#.###.###.#####.#.###.#####.###.#.#.###.#.### +#.#...#.#...#.#...#.#.#.#.....#.#.#...#...#...#...#...#.#...#...#.....#.#...#.........#.#...#...#...# +#.###.#.#.#.###.###.#.#.#####.#.#####.###.#.#.###.###.#.###.###.#####.###.###########.#.#####.#####.# +#.#.#$..#.$....$#...$.#.#$..#.$....$#...$...#$....$...#$..#.$.#..$#.#.$...#$....$...#$#.#.$...#$....# +#.#.#####.#######.###.#.#.#.#.#####.###.#########.#.#########.#.#.#.#######.#######.#.#.#.#####.###.# +#.#.....#.....#.#.#.#...#.#.#.#.....#.#.#.......#.#.#...#...#.#.#.......#...#.....#.#.#.....#...#.#.# +#.###.#.#####.#.#.#.#####.#.#.#.###.#.#.###.#####.#.#.#.#.#.#.#.#########.###.###.#.#########.###.#.# +#.....#.......#.#.......#.#.#...#.#.#.#...#.#.....#.#.#.#.#.#.#...#.......#...#...#...........#.#...# +#####$####$####$####$##.#$#.##$##.#$#.##$.#.#$####$.#.#$#.#.$.###$#.##$####$####$####$####$####$#.### +#.........#...#.......#...#.#.....#.....#.#.#.#...#...#...#...#...#...#.....#...................#.#.# +#.#.#####.#.#.#.#.#########.#.#.#####.###.#.#.#.#.###########.#.#####.#.#####.###.#############.#.#.# +#.#...#.#...#.#.#.#...#.....#.#.....#.#...#...#.#.......#...#...#.....#...#...#.#.....#...#...#.#...# +#####.#.#####.#.#.#.#.#.#########.#.#.#.#######.#######.#.#.#####.#######.#.###.#####.#.#.#.#.#####.# +#....$#.#.$.#..$#.#.$...#$....$...#$#.#.$....$..#.$....$#.#.$....$#...$...#$....$...#$..#.$.#..$..#.# +#.#####.#.#.#####.#.#####.###.#######.#.#.#######.#.#####.#########.#####.#####.#.#.#####.#.###.###.# +#.#.......#.....#.#.#...#.#.#.....#...#.#.#.......#.......#.............#...#...#.#.....#.#...#...#.# +#.###.#####.#####.#.###.#.#.#####.#.###.#.#.###.#.#########.###########.#####.#######.#.#.#.#####.#.# +#...#...#.#.......#...#.#...#...#.....#.#.#...#.#.....#...#.#.........#.#.....#.......#.#.#.#.....#.# +#.#.#$#.#.$####$####$.#.#$###.$.#.#$###.$.###$#.##$##.#$#.#.$.###$###.$.#.#$###.$####$#.#.$.#.#$#.#.# +#.#.#...#...........#.#...#...#.#...#...#.#...#.....#...#.#.#.......#.#...#.....#...#...#...#.#...#.# +#.#.#.###.#.#####.###.#.###.###.#####.#.#.#########.###.###.#######.###.###.###.#.#.#.#######.#.###.# +#.#.#.....#.#...#.#...#...#...#.#.....#.#.#.....#...#...#...#...#.#.#...#...#.#.#.#.#.....#...#.#...# +#.#.#########.#.###.#########.#.#.#####.#.#.###.#.#######.###.#.#.#.#.###.###.#.#.#.#######.#####.### +#.#..$#...$...#$....$....$#...$.#.#$..#.$...#$..#.$....$..#.$.#..$#.#.$.#..$#...$.#..$....$....$#...# +#####.#.#############.###.#.###.#.#.#.#.#####.###.#.#########.###.#.###.#.#.#.###.#######.#.###.###.# +#.....#.#...#.........#.#.#...#.#...#.#...#...#...#.....#.....#...#...#.#.#.#.#.#.#...#...#.#.....#.# +#.#.###.#.#.#.#########.#.#.#.#.#####.###.#.###.#######.#.###########.#.###.#.#.#.###.#.#####.#####.# +#.#.#.....#...#.........#.#.#.#.......#...#.#.........#.#...#.........#.....#...#...#.#.#.....#...#.# +#.###$####$####$####$##.#$###.$####$####$##.#$####$##.#$###.$.###$####$##.#$####$.#.#$#.#.$####$#.#.# +#.....#.......#.#.....#.#.....#.....#.......#.#.......#...#.#.....#.#.....#.....#.#.#...#.#...#.#...# +#######.#####.#.#.###.#####.#####.###.#######.#.#########.#.#####.#.#.#####.###.#.#.#.###.#.#.#.##### +#...#...#...#.#.#...#.......#...#.#...#.......#...........#.....#.#...#.....#.#.#.#.#...#.#.#.#...#.# +#.#.#.###.###.#.###.#########.#.###.#########.#############.#.#.#.#.###.#####.#.#.#.###.#.#.#.###.#.# +#.#..$#...$...#$#...$.#..$#...$...#$#...$...#$....$.#..$..#.$.#.#$#.#.$.#..$#...$.#.#$#.#.$.#..$..#.# +#.#####.#.#.###.#.#####.#.#.#####.#.#.#####.#######.#.###.###.#.#.###.###.#.#.#####.#.#.#.#.#######.# +#...#.#.#...#.....#.#...#.#.#...#.#...#.....#.......#...#.#...#.#...#.#...#.#.......#...#.#.#.....#.# +#.#.#.#.#########.#.#.###.#.#.#.#.#####.#####.#######.#.#.#.#######.#.#.###.###.#####.###.#.###.#.#.# +#.#.#.#.#.......#...#.#.#...#.#...#.....#...#.....#...#.#...#...#...#.....#...#.#...#.....#.....#.#.# +###.#$#.#.$####$####$.#.#$###.$####$####$.#.#$###.$.###$####$.#.#$###.$####$#.##$.#.#$####$####$#.#.# +#...#.....#.........#.#.#.....#...#.#.#...#...#...#...#.#.....#...#.#.#.....#.#...#.#.........#...#.# +#.#########.#######.#.#.#.#####.#.#.#.#.###.###.#######.#.#########.#.#.###.#.#.###.#.#####.#.#.###.# +#.......#...#.....#...#...#.....#...#.#.#.#.........#...#.....#.......#.#.#...#.#...#.#.#...#.#.#...# +#.#####.#####.###.#####.#######.#####.#.#.#########.#.#######.###.#####.#.###.#.#.#.#.#.#.#####.#.#.# +#...#$..#.$...#$..#.$.#..$..#.$.#..$....$...#$..#.$...#$....$...#$#.#.$.#..$..#.$.#.#$#.#.$....$#.#.# +###.#.#.#.#####.###.#.#####.#.###########.###.#.#####.#.#.#####.#.#.#.###.#####.#.###.#.#########.#.# +#...#.#.#.#...#.....#.....#...#.....#...#.#...#.#...#.#.#...#...#.#.....#.#.....#...#.#.......#...#.# +#.###.###.#.#.###########.#####.###.#.#.###.###.#.#.###.###.#.###.#.###.###.#######.#.#.#####.#.###.# +#...#.......#...........#.........#...#.......#...#.......#...#.......#...........#.....#.......#.... +##################################################################################################### + +##################################################################################################### +#.........#.........#...#.................#.....#.....#...........#...#.....#.....#...........#.....# +#####.###.#.#######.#.#.###.###.###########.#.###.#.#.###.#######.###.#.#.#.###.#.#####.#.###.#.###.# +#.....#...#.......#...#...#...#.#...#.......#.....#.#...#.....#.#...#...#.#...#.#.....#.#.#.....#.#.# +#.###############.#######.###.###.#.#.#############.###.#####.#.###.#####.###.#.#####.#.#.#.#####.#.# +#....$#...$....$#...$.#.#$..#.$...#$..#.$...#$....$...#$..#.$.#..$#...$...#$..#.$....$#.#.$.#..$#.#.# +#####.#.#.#.###.#####.#.###.#.#############.#.###.#.#.###.#.###.###.#######.###.#.#####.#.###.#.#.#.# +#.....#.#.....#.....#.#...#.#.....#.........#...#.#.#.#...#.#.......#...#.......#...#...#.#...#.#...# +#.###.#.#########.###.###.#.#####.###.###.#####.#.###.#.###.###.#####.#.###########.#####.#.###.#.### +#.#...#...#.....#.#.......#.....#.....#...#.....#...#.#...#...#.#.....#.#.........#.....#...#...#.#.# +#.###$###.$.###$#.#.$####$####$.###$###.$##.#$####$.#.#$#.##$.###$####$.#.#$####$.###$#.##$##.#$#.#.# +#...#.....#.#...#...#...#.....#.#.#.......#.#.....#...#.#...#...#...#...#.#...#.#.#.....#.....#...#.# +#.#.#.#####.###.###.#.#.#.###.#.#.#.#######.#.###.#####.###.#.#.###.#.#.#.#.#.#.#.###.###.#####.###.# +#.#.#.....#...#...#.#.#.....#...#...#.......#...#...#...#...#.#.#...#.#.#.#.#...#...#.#.....#.#.#...# +#.#.#.#######.###.###.#####.#####.###.#########.#.#.#.###.#####.#.###.#.#.#.#######.#.#####.#.#.#.#.# +#.#..$#...$...#$#...$.#..$#...$...#$..#.$....$..#.$...#$..#.$...#$..#.$.#..$#...$...#$#...$.#.#$#.#.# +#.#####.#######.#######.#.#.#.#.###.###.#########.#.###.###.#######.#.#####.#.#######.#.#.#.#.#.#.#.# +#.#...#.....#.......#...#.#.#...#...#...#.......#.#.#.......#.......#.....#.#.....#.....#...#.#...#.# +#.#.#.#####.#.###.#.###.#.#.#####.#####.###.###.#.###.#######.#########.###.#####.###########.#####.# +#...#.#...#.#.#...#...#.#...#...#.....#...#...#.#.....#.#...#...#.#...#...#.#...#.......#...#.....#.# +#####$#.#.$.###$####$.###$###.$.###$#.##$.###$#.##$####$#.#.$##.#$#.#.$##.#$#.##$##.#$#.#.$##.#$###.# +#.....#.#.#...#.#...#.........#.....#.#.#.....#.........#.#.....#...#...#.#.#.#...#...#.#...#.#.....# +#.#####.#.###.#.#.#.###############.#.#.#####.#.#######.#.###########.#.#.#.#.#.#.#####.###.#.####### +#.#.#...#...#.#...#...#...#.......#...#.....#...#.#.....#.............#.#...#.#.#.#.....#...#.....#.# +#.#.#.#####.#.#####.#.#.#.#######.#####.###.#####.#.###############.#######.#.#.#.#.#####.#.#####.#.# +#.#.#$..#.$...#$..#.$.#.#$....$.#..$..#.$.#..$#...$.#..$....$....$#.#.$...#$..#.$...#$....$...#$..#.# +#.#.###.#.#####.#.###.#.#######.#.###.###.#.###.#####.#######.#####.#.###.#####.#####.#.#######.###.# +#.#...#.#.#.....#...#...#.......#...#.....#.#...#.....#.....#.......#.#...#...#...#...#...#...#.#...# +#.#.#.#.###.#######.#####.#.#######.#####.#.#.###.#####.#############.###.#.#.###.###.###.#.#.#.###.# +#.#.#.#.....#.#.....#.....#.#.....#.#...#.#...#...#...........#.#...#...#...#...#...#...#...#.#.#...# +#.#.#$####$##.#$####$.###$#.#.$##.#$#.#.$####$#.##$####$###.$.#.#$#.##$.###$###.$##.#$#.##$##.#$#.#.# +#.#.#...#.....#.....#...#.#.#.#.#.....#.#...#.#...........#.#.#...#...#.#...........#.#.....#...#.#.# +#.#####.#.#.#.#####.#.#.#.#.#.#.#######.#.#.###.#########.#.#.#####.#.#.#############.#####.#####.### +#.....#...#.#.....#.#.#.#...#.#.......#.#.#...#.#.......#.#.#.......#.#...........#.......#.....#...# +#####.#.###.#.#######.#.###.#.#.#####.#.#.###.#.#.#####.#.#.#######.#####.#######.#.###.#.#####.#.#.# +#...#$#...$.#.#$....$.#..$#.#.$....$#.#.$.#.#$..#.$...#$#...$....$#.#.$.#..$....$.#.#$..#.$...#$#.#.# +###.#.###.#.###.###.#.###.###.#######.#.#.#.#####.#.###.#######.###.#.#.#######.#.###.#.###.#.#.###.# +#...#.#...#...#...#.#...#...#.#.......#...#.....#.#.....#.......#...#.#.#.....#.#.#...#.#...#.#.#...# +#.#.#.#.#####.###.#.###.###.#.#.#####.#######.###.#.#####.#####.#.###.#.#####.#.#.#.#####.###.#.#.### +#.#.#.#.#...#.....#.....#.#...#.....#.........#...#...#.......#.#.#.#.#.....#...#.#.........#.#.#...# +#.###$#.#.$.###$####$##.#$####$####$###.$####$#.##$##.#$####$.###$#.#.$####$#.##$.#.#$####$##.#$###.# +#.....#...#.#.........#.....#.....#...#.#.......#...#.#.....#.......#.....#.#...#.#...#.....#...#...# +#.#######.###.#.#####.#####.#.###.#.#.###.#######.#.#.###################.#.###.#.#####.###.###.#.#.# +#...#...#.#...#.#...#.....#...#...#.#...#.......#.#.#.......#.......#.....#.#...#.....#.#.#...#...#.# +###.###.#.#.#####.#.#####.#######.#####.#######.#.#.#######.#.#####.#.#####.#.#######.#.#.###.####### +#.#..$#.#.$....$..#.$...#$....$.#..$#...$...#$..#.$....$#...$....$#...$....$#...$....$#.#.$.#.#$....# +#.###.#.#.#########.#.#########.###.#.#.#####.###.#.#####.#######.#####.#####.###.#####.###.#.#.###.# +#.....#.#...#...#...#...#.....#.#.....#.#.....#.#.#...............#...#...#...#...#.........#...#...# +#.#####.###.###.#.###.#.#.#.###.#########.#####.#.###################.###.###.#.###.#######.#####.### +#.#.......#.....#.#...#...#...#.#.......#.#...#...#.......#...........#.#...#.#.......#...#.....#...# +#.###$###.$.###$#.#.$####$#.#.$.#.#$###.$.#.#$####$.###$#.#.$.###$###.$.###$#.##$####$#.#.$.###$#.#.# +#.#...#...#.#.....#...#.....#.#...#...#.#.#.#.....#.....#.#.#.#...#.#...#...#...#...#...#.#.#...#.#.# +#.#.#.#.###.#.#######.#.#####.#####.###.#.#.#####.#####.#.###.#.#.#.#.###.#####.#.###.###.#.#.#.###.# +#...#.#.....#.#.....#...#...#.......#...#...#...#.....#.#...#.#.#.#...#...#...#.#...#.#.#.#.#.#.#...# +#####.#.#####.#.###.#.###.#.#####.#.#.###.###.#.#.#.###.###.#.#.#.###.#.###.###.###.#.#.#.#.#.#.#.#.# +#...#$#...$...#$..#.$.#..$#...$...#$#.#.$...#$#.#.$.#..$#.#.$...#$..#.$.#..$....$...#$#...$.#.#$..#.# +#.###.###.#.###########.###########.#.#####.#.#.###.#.###.#.#.#####.###.#.#########.#.#.###.#.#####.# +#...#...#.#.#...........#...#...#...#...#...#.#...#.#.....#.#.....#.....#.#...#.....#.#...#.#.#.#...# +#.#.###.###.#.#.#########.#.#.#.#.#####.#####.###.#.###.###.#####.#######.#.#.#######.###.###.#.#.### +#.#...#.....#.#.......#...#...#.#.#...#.......#...#...#.#...#...#...#.....#.#...#.....#.#...#.#...#.# +#.###$####$##.#$####$.#.#$####$.#.#$#.##$####$#.##$.#.#$#.##$.###$#.##$####$###.$.###$#.##$.#.#$###.# +#...#...#.#...#.....#...#.....#.#.............#.#.#.#...#.#...#...#...#.....#.#.#.....#...#...#...#.# +#.###.#.#.#.###.#####.#######.#.#############.#.#.#.#####.#.###.#.###.#.#####.#.#####.#.#.#######.#.# +#.#...#.#.#.#...#...#...#.....#.#...........#.#.#...#...#.#.....#...#...#...#.#.......#.#.......#.#.# +###.###.#.#.#.#.#.#.###.#.###.#.#.###########.#.###.#.#.#.###.#####.###.#.#.#.#########.#######.#.#.# +#...#$#.#.$.#.#$#.#.$...#$..#.$.#..$....$...#$#...$.#.#$#...$....$#...$...#$#...$....$....$....$#...# +#.###.#.#.#.#.###.#.###.###.#.#.#########.#.#.###.###.#.###.#####.###.#####.###.#.#########.###.###.# +#.#...#.#.#.#.#...#...#.....#.#.....#...#.#...#...#...#.....#.....#...#.......#.......#.....#.....#.# +#.#.###.#.#.#.#.#####.#######.#####.#.#.#######.###.#########.#####.#########.#######.#.#####.#####.# +#.#.....#.....#...#.........#.....#.#.#.......#.....#.........#.....#...#...#.......#.#.....#.#.....# +#.###$####$##.#$#.##$####$#.##$##.#$#.##$####$####$####$####$##.#$###.$.#.#$####$##.#$#.##$.###$##### +#...#.......#...#.......#.......#.#.#.#.....#.........#...#...#.......#...#.#.....#...#.#.#...#.#...# +#.#.#######.#.#####.###.#.#####.###.#.###.#####.#####.#####.#.#######.#####.#####.###.#.#.###.#.#.#.# +#.#.......#.#.#...#.#...#.#...#.....#...#.#.....#...#...#...#.#.....#.....#.#...#...#.#...#...#...#.# +#.#######.#.#.#.#.###.#.###.#.#####.###.#.#.#######.###.#.###.#.###.#######.#.#.###.#.###.#.#.#####.# +#...#$..#.$.#.#$#...$.#.#$..#.$....$#.#.$.#..$....$....$#.#.$.#..$#...$....$#.#.$.#..$#...$.#.#$....# +###.###.#.#.#.#.###.#.###.###.###.###.#.#.#######.###.###.#.#.#.#.#####.#####.###.#.#######.###.##### +#.....#.#.#.#.#...#.#.#...#.#...#.#...#.#...#...#...#.#...#...#.#.#...#.#.....#.#.#.......#...#.#...# +#####.#.#.#.#.###.#.#.#.###.###.#.#.###.#.#.#.#.###.#.#.#.#.###.#.#.#.###.#####.#.#######.###.#.#.#.# +#...#...#.#.#.#...#...#.#.....#.#.#...#...#.#.#.#...#.#.#.#...#.#...#...........#.......#.#...#.#.#.# +#.#.#$###.$.###$####$.#.#$###.$.#.#$#.##$##.#$###.$##.#$#.##$.###$###.$####$####$##.#$#.#.$.#.#$#.#.# +#.#.......#.....#.#...#.#.#...#.#.....#...#.#.#...#.......#...#.....#.#.#.....#...#.#...#...#.#.#.#.# +#.#####.#########.#.###.###.#.#.###.###.#.#.#.#.###########.###.###.#.#.#.###.#.#.###.#######.#.#.#.# +#.#...#.#...#...#...#.#.#...#.#.#...#...#...#.#.....#.....#...#.#...#...#.#.....#.#...#.......#...#.# +#.#.#.###.#.#.#.#.###.#.#.###.#.###.#.#######.#####.#.#.#.###.#.###.###.#.#######.#.###.###########.# +#.#.#$....$.#.#$#.#.$.#..$#...$...#$#.#.$....$#...$.#.#$#.#.$.#..$#...$.#..$#...$...#$#.#.$...#$....# +#.#.#######.#.###.#.#.#####.#####.###.#.#.###.#.#.#.###.###.#####.###.#####.#########.#.#.###.#.##### +#...#.......#.....#.#.#.....#...#.#...#.#.#.#...#.#...#...#.......#.........#...#.......#.#.#.#.....# +#.###.###########.#.###.#######.#.#.#####.#.#####.###.#.#.#########.#########.#.#######.#.#.#.#####.# +#.#.#.......#...#...#...#.....#.#.#.#.....#.#.....#.#.#.#...........#.........#.#.....#.#...#.....#.# +#.#.#$####$.#.#$###.$.###$###.$.#.#$#.##$##.#$####$.#.#$####$##.#$###.$####$###.$.###$###.$##.#$###.# +#.#.......#...#.#.#.#.#...#.....#.#.#.#...#...#.....#.........#...#.#.#...#.....#...#.....#...#.....# +#.#.###.#######.#.#.#.###.#######.#.#.#.#.#.###.###.#########.###.#.#.###.#.#######.#######.#.#.##### +#...#...#...#...#...#...#.#.....#.#...#.#.#...#.#.......#...#...#...#.#...#.#.......#.......#.#.....# +###.#####.#.#.###.#####.#.#.###.#.#####.#####.#.#######.#.#.#.#.#####.#.###.#.#######.#############.# +#...#$..#.$...#$....$...#$#.#.$...#$..#.$....$#...$.#..$#.#.$.#..$..#.$.#..$#.#.$...#$..#.$...#$....# +#.###.#.#.#########.#.###.#.#######.#.#.#######.#.#.#.###.#.#######.###.#.###.#.###.###.#.###.#.##### +#...#.#...#...#...#.#...#.#.#.......#...#.....#.#.#.#.#.#.#.........#...#...#.#...#.#.#.#.#...#.#...# +###.#.#####.#.#.#.#####.#.#.#.###########.###.###.#.#.#.#.###########.#####.#.###.#.#.#.#.#.###.#.#.# +#...#.......#...#.........#...............#.......#.....#.................#.......#...#...#.......#.# +##################################################################################################### \ No newline at end of file diff --git a/gleam/codingquest2024/src/day9/solution.gleam b/gleam/codingquest2024/src/day9/solution.gleam new file mode 100644 index 0000000..e69de29 diff --git a/gleam/codingquest2024/src/utilities/memo.gleam b/gleam/codingquest2024/src/utilities/memo.gleam new file mode 100644 index 0000000..b06d8fd --- /dev/null +++ b/gleam/codingquest2024/src/utilities/memo.gleam @@ -0,0 +1,57 @@ +import gleam/dict.{type Dict} +import gleam/otp/actor.{type Next, Continue, Stop} +import gleam/erlang/process.{type Subject, Normal} +import gleam/option.{None} + +const timeout = 1000 + +type Message(k, v) { + Shutdown + Get(key: k, client: Subject(Result(v, Nil))) + Set(key: k, value: v) +} + +type Server(k, v) = + Subject(Message(k, v)) + +pub opaque type Cache(k, v) { + Cache(server: Server(k, v)) +} + +fn handle_message( + message: Message(k, v), + dict: Dict(k, v), +) -> Next(Message(k, v), Dict(k, v)) { + case message { + Shutdown -> Stop(Normal) + Get(key, client) -> { + process.send(client, dict.get(dict, key)) + Continue(dict, None) + } + Set(key, value) -> Continue(dict.insert(dict, key, value), None) + } +} + +pub fn create(apply fun: fn(Cache(k, v)) -> t) -> t { + let assert Ok(server) = actor.start(dict.new(), handle_message) + let result = fun(Cache(server)) + process.send(server, Shutdown) + result +} + +pub fn set(in cache: Cache(k, v), for key: k, insert value: v) -> Nil { + process.send(cache.server, Set(key, value)) +} + +pub fn get(from cache: Cache(k, v), fetch key: k) -> Result(v, Nil) { + process.call(cache.server, fn(c) { Get(key, c) }, timeout) +} + +pub fn memoize(with cache: Cache(k, v), this key: k, apply fun: fn() -> v) -> v { + let result = case get(from: cache, fetch: key) { + Ok(value) -> value + Error(Nil) -> fun() + } + set(in: cache, for: key, insert: result) + result +} diff --git a/gleam/codingquest2024/test/codingquest2024_test.gleam b/gleam/codingquest2024/test/codingquest2024_test.gleam new file mode 100644 index 0000000..3831e7a --- /dev/null +++ b/gleam/codingquest2024/test/codingquest2024_test.gleam @@ -0,0 +1,12 @@ +import gleeunit +import gleeunit/should + +pub fn main() { + gleeunit.main() +} + +// gleeunit test functions end in `_test` +pub fn hello_world_test() { + 1 + |> should.equal(1) +} diff --git a/leetcode/lc-1018-binary-prefix.rkt b/leetcode/lc-1018-binary-prefix.rkt deleted file mode 100644 index fa82681..0000000 --- a/leetcode/lc-1018-binary-prefix.rkt +++ /dev/null @@ -1,11 +0,0 @@ -#lang racket -(define/contract (prefixes-div-by5 A) - (-> (listof exact-integer?) (listof boolean?)) - (define ns (make-vector (length A) #false)) - (for/fold ([acc 0]) - ([b (in-list A)] - [i (in-naturals)]) - (let ([test-val (remainder (+ (* 2. acc) b) 5)]) - (when (= 0 test-val) (vector-set! ns i #true)) - test-val)) - (vector->list ns)) \ No newline at end of file diff --git a/leetcode/lc-1037-boomerang.rkt b/leetcode/lc-1037-boomerang.rkt deleted file mode 100644 index fd95695..0000000 --- a/leetcode/lc-1037-boomerang.rkt +++ /dev/null @@ -1,21 +0,0 @@ -#lang racket -(define/contract (is-boomerang points) - (-> (listof (listof exact-integer?)) boolean?) - (match points - [(list-no-order a b c) #:when (equal? a b) #false] ; Are any two points the same? - [(list (list x _) (list x _) (list x _)) #false] ; Are they on a horizontal line? - [(list (list _ y) (list _ y) (list _ y)) #false] ; Are they on a vertical line? - [(list-no-order (list x1 _) (list x2 _) (list x3 _)) ; Are two points on a horizontal line, - #:when (and (= x1 x2) ; but the third point isn't? - (not (= x1 x3))) #true] - [(list-no-order (list _ y1) (list _ y2) (list _ y3)) ; Are two points on a vertical line, - #:when (and (= y1 y2) ; but the third point isn't? - (not (= y1 y3))) #true] - [(list (list x1 y1) (list x2 y2) (list x3 y3)) ; If none of the special cases apply, - (let ([m (/ (- y2 y1) (- x2 x1))]) ; calculate the slope between two points - (not (= y3 (+ y1 (* m (- x3 x1))))))])) ; and see if the line passes through the third - -(is-boomerang '((1 1) (2 3) (3 2))) -(is-boomerang '((1 1) (2 2) (3 3))) -(is-boomerang '((0 0) (0 2) (2 1))) -(is-boomerang '((0 0) (1 1) (1 1))) \ No newline at end of file diff --git a/leetcode/lc-1185-day-of-week.rkt b/leetcode/lc-1185-day-of-week.rkt deleted file mode 100644 index c90a626..0000000 --- a/leetcode/lc-1185-day-of-week.rkt +++ /dev/null @@ -1,18 +0,0 @@ -#lang racket -(require racket/date) - -(define day-names - (for/hash ([day-number (in-range 0 7)] - [day-name (in-list '("Sunday" - "Monday" - "Tuesday" - "Thursday" - "Friday" - "Saturday"))]) - (values day-number day-name))) - -(define/contract (day-of-the-week day month year) - (-> exact-integer? exact-integer? exact-integer? string?) - (hash-ref day-names (date-week-day - (seconds->date - (find-seconds 0 0 0 day month year))))) diff --git a/leetcode/lc-1207-unique-occurences.rkt b/leetcode/lc-1207-unique-occurences.rkt deleted file mode 100644 index 1b4d107..0000000 --- a/leetcode/lc-1207-unique-occurences.rkt +++ /dev/null @@ -1,10 +0,0 @@ -#lang racket -(define/contract (unique-occurrences arr) - (-> (listof exact-integer?) boolean?) - (define occurrences (make-hash)) - (for ([n (in-list arr)]) - (hash-update! occurrences n add1 1)) - (equal? (hash-values occurrences) - (remove-duplicates (hash-values occurrences)))) - -(unique-occurrences '(1 2 2 1 1 3)) \ No newline at end of file diff --git a/leetcode/lc-1221-split-a-string-balanced.rkt b/leetcode/lc-1221-split-a-string-balanced.rkt deleted file mode 100644 index 4c75770..0000000 --- a/leetcode/lc-1221-split-a-string-balanced.rkt +++ /dev/null @@ -1,19 +0,0 @@ -#lang racket -(require rackunit) - -(define/contract (balanced-string-split s) - (-> string? exact-integer?) - (for/fold ([acc 0] - [count 0] - #:result count) - ([c (string->list s)]) - (let* ([increment (case c - [(#\R) 1] - [(#\L) -1])] - [new-acc (+ increment acc)] - [new-count (case new-acc - [(0) (add1 count)] - [else count])]) - (values new-acc new-count)))) - -(check-eq? (balanced-string-split "RLRRLLRLRL") 4) \ No newline at end of file diff --git a/leetcode/lc-125-valid-palindrome.rkt b/leetcode/lc-125-valid-palindrome.rkt deleted file mode 100644 index ed91d08..0000000 --- a/leetcode/lc-125-valid-palindrome.rkt +++ /dev/null @@ -1,12 +0,0 @@ -#lang racket - -(define/contract (is-palindrome s) - (-> string? boolean?) - (define clean-string - (string-downcase (string-replace s #rx"[^A-Za-z0-9]" ""))) - (string-prefix? (apply string-append (map string (reverse (string->list clean-string)))) - (substring clean-string - 0 - (ceiling (/ (string-length clean-string) 2))))) - -(is-palindrome "A man, a plan, a canal: Panama") \ No newline at end of file diff --git a/leetcode/lc-1295-even-number-of-digits.rkt b/leetcode/lc-1295-even-number-of-digits.rkt deleted file mode 100644 index 9e88454..0000000 --- a/leetcode/lc-1295-even-number-of-digits.rkt +++ /dev/null @@ -1,4 +0,0 @@ -#lang racket -(define/contract (find-numbers nums) - (-> (listof exact-integer?) exact-integer?) - (count (λ (n) (odd? (order-of-magnitude n))) nums)) \ No newline at end of file diff --git a/leetcode/lc-1299-replace-with-greatest-to-right.rkt b/leetcode/lc-1299-replace-with-greatest-to-right.rkt deleted file mode 100644 index 34d3eae..0000000 --- a/leetcode/lc-1299-replace-with-greatest-to-right.rkt +++ /dev/null @@ -1,8 +0,0 @@ -#lang racket -(define/contract (replace-elements arr) - (-> (listof exact-integer?) (listof exact-integer?)) - (cond [(= 1 (length arr)) '(-1)] - [else (cons (apply max (cdr arr)) - (replace-elements (cdr arr)))])) - -(replace-elements '(17 18 5 4 6 1)) \ No newline at end of file diff --git a/leetcode/lc-1304-find-n-unique-integers.rkt b/leetcode/lc-1304-find-n-unique-integers.rkt deleted file mode 100644 index 9b810a0..0000000 --- a/leetcode/lc-1304-find-n-unique-integers.rkt +++ /dev/null @@ -1,5 +0,0 @@ -#lang racket -(define/contract (sum-zero n) - (-> exact-integer? (listof exact-integer?)) - (cond [(even? n) (remove 0 (range (/ n -2) (add1 (/ n 2))))] - [(odd? n) (range (/ (- n 1) -2) (add1 (/ (- n 1) 2)))])) \ No newline at end of file diff --git a/leetcode/lc-1436-destination-city.rkt b/leetcode/lc-1436-destination-city.rkt deleted file mode 100644 index ce82f08..0000000 --- a/leetcode/lc-1436-destination-city.rkt +++ /dev/null @@ -1,14 +0,0 @@ -#lang racket -(define/contract (dest-city paths) - (-> (listof (listof string?)) string?) - (define city-pairs (make-hash paths)) - (define (go-to-next-city origin) - (let ([destination (hash-ref city-pairs origin #false)]) - (if destination - (go-to-next-city (car destination)) - origin))) - (go-to-next-city (caar paths))) - -(dest-city '(("London" "New York") - ("New York" "Lima") - ("Lima" "Sao Paolo"))) \ No newline at end of file diff --git a/leetcode/lc-1450-students-doing-homework.rkt b/leetcode/lc-1450-students-doing-homework.rkt deleted file mode 100644 index 14ff079..0000000 --- a/leetcode/lc-1450-students-doing-homework.rkt +++ /dev/null @@ -1,12 +0,0 @@ -#lang racket -(define/contract (busy-student start-time end-time query-time) - (-> (listof exact-integer?) (listof exact-integer?) exact-integer? exact-integer?) - (count (λ (start end) - (and (start . <= . query-time) - (query-time . <= . end))) start-time end-time)) - -(busy-student '(1 2 3) '(3 2 7) 4) -(busy-student '(4) '(4) 4) -(busy-student '(9 8 7 6 5 4 3 2 1) - '(10 10 10 10 10 10 10 10 10) - 5) \ No newline at end of file diff --git a/leetcode/lc-1460-make-two-arrays-equal.rkt b/leetcode/lc-1460-make-two-arrays-equal.rkt deleted file mode 100644 index 584ac97..0000000 --- a/leetcode/lc-1460-make-two-arrays-equal.rkt +++ /dev/null @@ -1,4 +0,0 @@ -#lang racket -(define/contract (can-be-equal target arr) - (-> (listof exact-integer?) (listof exact-integer?) boolean?) - (equal? (sort target <) (sort arr <))) \ No newline at end of file diff --git a/leetcode/lc-1496-path-crossing.rkt b/leetcode/lc-1496-path-crossing.rkt deleted file mode 100644 index 9c1941d..0000000 --- a/leetcode/lc-1496-path-crossing.rkt +++ /dev/null @@ -1,25 +0,0 @@ -#lang racket -(define/contract (is-path-crossing path) - (-> string? boolean?) - (for/fold ([current-x 0] - [current-y 0] - [trail (set '(0 0))] - [check #false] - #:result check) - ([step (in-list (string->list path))] - #:break check) - (let*-values - ([(new-x new-y) - (case step - [(#\N) (values current-x (add1 current-y))] - [(#\S) (values current-x (sub1 current-y))] - [(#\E) (values (add1 current-x) current-y)] - [(#\W) (values (sub1 current-x) current-y)])] - [(new-trail-point) (list new-x new-y)]) - (cond [(set-member? trail new-trail-point) - (values void void void #true)] - [else - (values new-x - new-y - (set-add trail new-trail-point) - #false)])))) \ No newline at end of file diff --git a/leetcode/lc-1700-students-unable-to-eat.rkt b/leetcode/lc-1700-students-unable-to-eat.rkt deleted file mode 100644 index 75cc243..0000000 --- a/leetcode/lc-1700-students-unable-to-eat.rkt +++ /dev/null @@ -1,36 +0,0 @@ -#lang racket -(define/contract (count-students students sandwiches) - (-> (listof exact-integer?) (listof exact-integer?) exact-integer?) - (for/fold ([sandwich-pile sandwiches] - [student-line students] - [remaining-students (length students)] - [break? #false] - #:result remaining-students) - ([i (in-naturals)] - #:break break?) - (cond [(and (empty? sandwich-pile) - (empty? student-line)) - (values void - void - remaining-students - #true)] - [(equal? (car sandwich-pile) - (car student-line)) - (values (cdr sandwich-pile) - (cdr student-line) - (sub1 remaining-students) - #false)] - [(and (not (equal? (list (car sandwich-pile)) - (remove-duplicates student-line))) - (= 1 (length (remove-duplicates student-line)))) - (values void - void - remaining-students - #true)] - [else - (values sandwich-pile - (append (cdr student-line) (list (car student-line))) - remaining-students - #false)]))) - -(count-students '(1 1 0 0) '(0 1 0 1)) diff --git a/leetcode/lc-1812-chessboard-square.rkt b/leetcode/lc-1812-chessboard-square.rkt deleted file mode 100644 index 206392c..0000000 --- a/leetcode/lc-1812-chessboard-square.rkt +++ /dev/null @@ -1,7 +0,0 @@ -#lang racket -(define/contract (square-is-white coordinates) - (-> string? boolean?) - (define file (first (string->list coordinates))) - (define rank (second (string->list coordinates))) - (or (and (odd? (char->integer file)) (even? (char->integer rank))) - (and (even? (char->integer file)) (odd? (char->integer rank))))) \ No newline at end of file diff --git a/leetcode/lc-1844-replace-all-digits-with-characters.rkt b/leetcode/lc-1844-replace-all-digits-with-characters.rkt deleted file mode 100644 index 96aba6e..0000000 --- a/leetcode/lc-1844-replace-all-digits-with-characters.rkt +++ /dev/null @@ -1,18 +0,0 @@ -#lang racket -(define/contract (replace-digits s) - (-> string? string?) - (define/contract (shift-letter c x) - (-> char? char? char?) - (integer->char (+ (string->number (string x)) (char->integer c)))) - (define letters (string->list (string-replace s #rx"[0-9]" ""))) - (define digits (string->list (string-replace s #rx"[a-z]" ""))) - (foldl (λ (c x acc) - (if (equal? x #\X) - (string-append acc (string c)) - (string-append acc (string c) (string (shift-letter c x))))) - "" - letters - (if (= (length digits) (length letters)) - digits - (append digits '(#\X))) - )) \ No newline at end of file diff --git a/leetcode/lc-1854-max-pop-year.rkt b/leetcode/lc-1854-max-pop-year.rkt deleted file mode 100644 index 75104f1..0000000 --- a/leetcode/lc-1854-max-pop-year.rkt +++ /dev/null @@ -1,17 +0,0 @@ -#lang racket -(define/contract (maximum-population logs) - (-> (listof (listof exact-integer?)) exact-integer?) - ; make a hash table of every year encountered between the birth and death years - (define population (make-hash)) - ; for each person in the logs, - (for/list ([person (in-list logs)]) - ; for every year from birth to the year before death, - (for/list ([year (in-range (first person) (second person))]) - ; look up the year in the hash table and add 1 to its key, - ; or add the key and set its value to 1 if it doesn't exist yet - (hash-update! population year add1 1))) - ; convert the hash table to a list, - ; sort the list by year, - ; find the first element that maximizes the count, - ; and return the associated year - (car (argmax cdr (sort (hash->list population) < #:key car)))) \ No newline at end of file diff --git a/leetcode/lc-2-add-two-numbers.rkt b/leetcode/lc-2-add-two-numbers.rkt deleted file mode 100644 index 8062817..0000000 --- a/leetcode/lc-2-add-two-numbers.rkt +++ /dev/null @@ -1,35 +0,0 @@ -#lang racket -; Definition for singly-linked list: - - -; val : integer? -; next : (or/c list-node? #f) -(struct list-node - (val next) #:mutable #:transparent) - -; constructor -(define (make-list-node val [next-node #f]) - (list-node val next-node)) - - -(define/contract (add-two-numbers l1 l2) - (-> (or/c list-node? #f) (or/c list-node? #f) (or/c list-node? #f)) - (define (process-list node [acc '()]) - (if (list-node-next node) - (process-list (list-node-next node) (cons (list-node-val node) acc)) - (cons (list-node-val node) acc))) - (define sum-of-lists (+ (string->number (apply ~a (process-list l1))) - (string->number (apply ~a (process-list l2))))) - (define sum-list-digits - (reverse - (map (λ (x) (string->number (string x))) - (string->list (number->string sum-of-lists))))) - (define (build-list l) - (if (empty? l) - #f - (make-list-node (car l) (build-list (cdr l))))) - (build-list sum-list-digits)) - -(define list1 (make-list-node 2 (make-list-node 4 (make-list-node 3)))) -(define list2 (make-list-node 5 (make-list-node 6 (make-list-node 4)))) -(add-two-numbers list1 list2) \ No newline at end of file diff --git a/leetcode/lc-217-contains-duplicate.rkt b/leetcode/lc-217-contains-duplicate.rkt deleted file mode 100644 index ca8d193..0000000 --- a/leetcode/lc-217-contains-duplicate.rkt +++ /dev/null @@ -1,12 +0,0 @@ -#lang racket -(define/contract (contains-duplicate nums) - (-> (listof exact-integer?) boolean?) - (define nums-hash (make-hash)) - (define (check-next-number nums) - (cond [(empty? nums) #false] - [(hash-ref nums-hash (car nums) #false) #true] - [else (hash-set! nums-hash (car nums) #true) - (check-next-number (cdr nums))])) - (check-next-number nums)) - -(contains-duplicate '(1 2 3)) \ No newline at end of file diff --git a/leetcode/lc-228-summary-ranges.rkt b/leetcode/lc-228-summary-ranges.rkt deleted file mode 100644 index 9140895..0000000 --- a/leetcode/lc-228-summary-ranges.rkt +++ /dev/null @@ -1,27 +0,0 @@ -#lang racket -(define (summary-ranges nums) - (define range-pairs - (cond - [(empty? nums) '()] - [(empty? (cdr nums)) (list (cons (car nums) (car nums)))] - [else (for/fold ([ranges '()] - [open-pair (first nums)] - [prev-num (first nums)] - #:result (append ranges (list (cons open-pair prev-num)))) - ([i (cdr nums)]) - (cond [(= (add1 prev-num) i) - (values ranges - open-pair - i)] - [else - (values (append ranges (list (cons open-pair prev-num))) - i - i)]))])) - (for/list ([p (in-list range-pairs)]) - (cond [(= (car p) (cdr p)) (format "~a" (car p))] - [else (format "~a->~a" (car p) (cdr p))]))) - -(summary-ranges '(0 1 2 4 5 7)) -(summary-ranges '(0 2 3 4 6 8 9)) -(summary-ranges '()) -(summary-ranges '(0)) \ No newline at end of file diff --git a/leetcode/lc-290-word-pattern.rkt b/leetcode/lc-290-word-pattern.rkt deleted file mode 100644 index 77cdba0..0000000 --- a/leetcode/lc-290-word-pattern.rkt +++ /dev/null @@ -1,23 +0,0 @@ -#lang racket -(define match-string "abba") -(define a "dog") -(define b "cat") -(define Σ "dog cat cat dog") - -(define/contract (word-pattern pattern s) - (-> string? string? boolean?) - (define pattern-list (map string (string->list pattern))) - (define s-list (string-split s)) - (define match-hash (make-hash)) - (if (= (length pattern-list) (length s-list)) - (for/and ([pattern-part pattern-list] - [s-part s-list]) - (cond [(and (not (hash-has-key? match-hash pattern-part)) - (member s-part (hash-values match-hash))) #f] - [(not (hash-has-key? match-hash pattern-part)) - (hash-set! match-hash pattern-part s-part) #t] - [(string=? (hash-ref match-hash pattern-part) s-part) #t] - [else #f])) - #f)) - -(word-pattern match-string Σ) \ No newline at end of file diff --git a/leetcode/lc-345-reverse-vowels.rkt b/leetcode/lc-345-reverse-vowels.rkt deleted file mode 100644 index c05bf2d..0000000 --- a/leetcode/lc-345-reverse-vowels.rkt +++ /dev/null @@ -1,9 +0,0 @@ -#lang racket - -(define/contract (reverse-vowels s) - (-> string? string?) - (define vowels-only - (string-replace s #rx"[^aeiouAEIOU]" "")) - (define consonants-with-placeholders - (string-replace s #rx"[aeiouAEIOU]" "~a")) - (apply format consonants-with-placeholders (reverse (string->list vowels-only)))) \ No newline at end of file diff --git a/leetcode/lc-349-intersection-of-2-arrays.rkt b/leetcode/lc-349-intersection-of-2-arrays.rkt deleted file mode 100644 index 14d56ca..0000000 --- a/leetcode/lc-349-intersection-of-2-arrays.rkt +++ /dev/null @@ -1,5 +0,0 @@ -#lang racket - -(define/contract (intersection nums1 nums2) - (-> (listof exact-integer?) (listof exact-integer?) (listof exact-integer?)) - (set-intersect nums1 nums2)) \ No newline at end of file diff --git a/leetcode/lc-36-valid-sudoku.rkt b/leetcode/lc-36-valid-sudoku.rkt deleted file mode 100644 index 915b533..0000000 --- a/leetcode/lc-36-valid-sudoku.rkt +++ /dev/null @@ -1,33 +0,0 @@ -#lang racket - -(define (pos board r c) - (list-ref (list-ref board r) c)) - -(define (scan-for-duplicates array) - (andmap (λ (row) (not (check-duplicates row))) - (map (curry filter-not (curry equal? ".")) array))) - -(define (check-rows board) - (scan-for-duplicates board)) - -(define (check-cols board) - (scan-for-duplicates (apply map list board))) - -(define (check-boxes board) - (define boxes-to-lists - (for*/list ([r (in-list '(0 3 6))] - [c (in-list '(0 3 6))]) - (for*/list ([box-r (in-range r (+ r 3))] - [box-c (in-range c (+ c 3))] - #:unless (equal? "." (pos board box-r box-c))) - (pos board box-r box-c)))) - (scan-for-duplicates boxes-to-lists)) - -(define/contract (is-valid-sudoku board) - (-> (listof (listof string?)) boolean?) - (and (check-rows board) - (check-cols board) - (check-boxes board))) - -(define valid-sudoku '[["5" "3" "." "." "7" "." "." "." "."] ["6" "." "." "1" "9" "5" "." "." "."] ["." "9" "8" "." "." "." "." "6" "."] ["8" "." "." "." "6" "." "." "." "3"] ["4" "." "." "8" "." "3" "." "." "1"] ["7" "." "." "." "2" "." "." "." "6"] ["." "6" "." "." "." "." "2" "8" "."] ["." "." "." "4" "1" "9" "." "." "5"] ["." "." "." "." "8" "." "." "7" "9"]]) -(define invalid-sudoku '[["8" "3" "." "." "7" "." "." "." "."] ["6" "." "." "1" "9" "5" "." "." "."] ["." "9" "8" "." "." "." "." "6" "."] ["8" "." "." "." "6" "." "." "." "3"] ["4" "." "." "8" "." "3" "." "." "1"] ["7" "." "." "." "2" "." "." "." "6"] ["." "6" "." "." "." "." "2" "8" "."] ["." "." "." "4" "1" "9" "." "." "5"] ["." "." "." "." "8" "." "." "7" "9"]]) \ No newline at end of file diff --git a/leetcode/lc-415-add-strings.rkt b/leetcode/lc-415-add-strings.rkt deleted file mode 100644 index e140155..0000000 --- a/leetcode/lc-415-add-strings.rkt +++ /dev/null @@ -1,28 +0,0 @@ -#lang racket - -(define/contract (add-strings num1 num2) - (-> string? string? string?) - (define (char->integer c) - ((compose string->number string) c)) - (define pad-length - (add1 (apply max (map string-length (list num1 num2))))) - (define (pad-with-zeroes n) - (~a n - #:align 'right - #:min-width pad-length - #:pad-string "0")) - (define (string-reverse s) - ((compose list->string reverse string->list) s)) - (define raw-sum - (for/fold ([sum-string ""] - [carry 0] - #:result sum-string) - ([n1 (string-reverse (pad-with-zeroes num1))] - [n2 (string-reverse (pad-with-zeroes num2))]) - (let* ([digit-sum (+ carry (char->integer n1) (char->integer n2))] - [sum-place (number->string (modulo digit-sum 10))] - [sum-carry (quotient digit-sum 10)]) - (values (string-append sum-place sum-string) - sum-carry)))) - (cond [(equal? raw-sum "00") "0"] - [else (string-trim raw-sum "0" #:repeat? #t #:right? #f)])) \ No newline at end of file diff --git a/leetcode/lc-43-multiply-strings.rkt b/leetcode/lc-43-multiply-strings.rkt deleted file mode 100644 index dac8c31..0000000 --- a/leetcode/lc-43-multiply-strings.rkt +++ /dev/null @@ -1,28 +0,0 @@ -#lang racket - -(define/contract (char-digit->integer c) - (-> char? integer?) - (- (char->integer c) 48)) - -(define/contract (integer->string-digit n) - (-> integer? string?) - (string (integer->char (+ n 48)))) - -(define/contract (number->string1 n [acc ""]) - (->* (integer?) (string?) string?) - (cond [(and (= n 0) (equal? acc "")) "0"] - [(= n 0) acc] - [else (number->string1 - (quotient n 10) - (string-append (integer->string-digit (remainder n 10)) acc))])) - -(define/contract (multiply num1 num2) - (-> string? string? string?) - (define multiplication-steps - (for/list ([n1 (in-string num1)] - [place1 (in-range (sub1 (string-length num1)) -1 -1)]) - (for/list ([n2 (in-string num2)] - [place2 (in-range (sub1 (string-length num2)) -1 -1)]) - (apply * (append (map char-digit->integer (list n1 n2)) - (list (expt 10 place1) (expt 10 place2))))))) - (number->string1 (apply + (flatten multiplication-steps)))) \ No newline at end of file diff --git a/leetcode/lc-476-number-complement.rkt b/leetcode/lc-476-number-complement.rkt deleted file mode 100644 index 724bb47..0000000 --- a/leetcode/lc-476-number-complement.rkt +++ /dev/null @@ -1,11 +0,0 @@ -#lang racket - -(define (flip-bit bit) - (cond [(char=? bit #\1) #\0] - [(char=? bit #\0) #\1])) - -(define/contract (find-complement num) - (-> exact-integer? exact-integer?) - (define num-binary-list - (string->list (number->string num 2))) - (string->number (apply ~a (map flip-bit num-binary-list)) 2)) \ No newline at end of file diff --git a/leetcode/lc-500-keyboard-row.rkt b/leetcode/lc-500-keyboard-row.rkt deleted file mode 100644 index 5f13143..0000000 --- a/leetcode/lc-500-keyboard-row.rkt +++ /dev/null @@ -1,23 +0,0 @@ -#lang racket - -(define keyboard-rows (list "qwertyuiop" - "asdfghjkl" - "zxcvbnm")) - -(define keyboard-row-sets - (for/list ([row keyboard-rows]) - (list->set (map string (string->list row))))) - -(define/contract (find-words words) - (-> (listof string?) (listof string?)) - (define word-checks - (for/list ([w words]) - (define word-set - (list->set (map string (string->list (string-downcase w))))) - (if (for/or ([row keyboard-row-sets]) - (subset? word-set row)) - w - '()))) - (filter-not empty? word-checks)) - -(find-words '("Hello" "Alaska" "Dad" "Peace")) \ No newline at end of file diff --git a/leetcode/lc-504-base7.rkt b/leetcode/lc-504-base7.rkt deleted file mode 100644 index 3e75052..0000000 --- a/leetcode/lc-504-base7.rkt +++ /dev/null @@ -1,16 +0,0 @@ -#lang racket - -(define/contract (convert-to-base7 num) - (-> exact-integer? string?) - (define (max-base-power n base [pow 1]) - (cond [(n . = . (expt base pow)) pow] - [(n . < . (expt base pow)) (sub1 pow)] - [else (max-base-power n base (add1 pow))])) - (define (add-next-digit n pow acc) - (cond [(= pow 0) (string-append acc (number->string n))] - [else (add-next-digit (remainder n (expt 7 pow)) - (sub1 pow) - (string-append acc - (number->string (quotient n (expt 7 pow)))))])) - (string-append (if (negative? num) "-" "") - (add-next-digit (abs num) (max-base-power (abs num) 7) ""))) \ No newline at end of file diff --git a/leetcode/lc-520-detect-capital.rkt b/leetcode/lc-520-detect-capital.rkt deleted file mode 100644 index 80b5f7e..0000000 --- a/leetcode/lc-520-detect-capital.rkt +++ /dev/null @@ -1,12 +0,0 @@ -#lang racket - -(define/contract (detect-capital-use word) - (-> string? boolean?) - (if - (member word (list (string-upcase word) - (string-downcase word) - (string-titlecase word))) - #true - #false)) - -(detect-capital-use "Google") \ No newline at end of file diff --git a/leetcode/lc-551-student-attendance-record-1.rkt b/leetcode/lc-551-student-attendance-record-1.rkt deleted file mode 100644 index c5f1456..0000000 --- a/leetcode/lc-551-student-attendance-record-1.rkt +++ /dev/null @@ -1,11 +0,0 @@ -#lang racket - -(define/contract (check-record s) - (-> string? boolean?) - (define s-list (map string (string->list s))) - (cond [(<= 2 (count (curry string=? "A") s-list)) #false] - [(string-contains? s "LLL") #false] - [else #true])) - -(check-record "PPALLP") -(check-record "PPALLL") \ No newline at end of file diff --git a/leetcode/lc-58-length-of-last-word.rkt b/leetcode/lc-58-length-of-last-word.rkt deleted file mode 100644 index 716df90..0000000 --- a/leetcode/lc-58-length-of-last-word.rkt +++ /dev/null @@ -1,7 +0,0 @@ -#lang racket - -(define/contract (length-of-last-word s) - (-> string? exact-integer?) - (if (empty? (string-split s)) - 0 - (string-length (last (string-split s))))) \ No newline at end of file diff --git a/leetcode/lc-645-set-mismatch.rkt b/leetcode/lc-645-set-mismatch.rkt deleted file mode 100644 index a9d9a61..0000000 --- a/leetcode/lc-645-set-mismatch.rkt +++ /dev/null @@ -1,24 +0,0 @@ -#lang racket - -(define/contract (find-error-nums nums) - (-> (listof exact-integer?) (listof exact-integer?)) - (define nums-set (list->set nums)) - (define range-set (apply set (range 1 (+ 2 (set-count nums-set))))) - (define missing-num (first (set->list (set-subtract range-set nums-set)))) - (define necessary-num - (if (set-member? nums-set (- missing-num 1)) - (+ missing-num 1) - (- missing-num 1))) - (list missing-num necessary-num)) - -(find-error-nums '(1 2 2 4)) - -(define fact-stream - (letrec ([f (lambda (x y) - (cond - [(zero? (modulo (- y 1) 3)) (cons (* 3 x) (lambda() (f (* x - y) (+ y 1))))] - [else (cons x (lambda() (f (* x y) (+ y 1))))]) - [else (cons x (lambda() (f (* x y) (+ y 1))))] - (lambda () (f 1 2)) - )]))) \ No newline at end of file diff --git a/leetcode/lc-657-robot-return.rkt b/leetcode/lc-657-robot-return.rkt deleted file mode 100644 index 908605a..0000000 --- a/leetcode/lc-657-robot-return.rkt +++ /dev/null @@ -1,17 +0,0 @@ -#lang racket - -(define/contract (judge-circle moves) - (-> string? boolean?) - (equal? '(0 0) - (for/fold ([y-pos 0] - [x-pos 0] - #:result (list y-pos x-pos)) - ([move (map string (string->list moves))]) - (values (case move - [("U") (add1 y-pos)] - [("D") (sub1 y-pos)] - [else y-pos]) - (case move - [("L") (add1 x-pos)] - [("R") (sub1 x-pos)] - [else x-pos]))))) \ No newline at end of file diff --git a/leetcode/lc-68-justification.rkt b/leetcode/lc-68-justification.rkt deleted file mode 100644 index 537e2c5..0000000 --- a/leetcode/lc-68-justification.rkt +++ /dev/null @@ -1,44 +0,0 @@ -#lang racket -(define/contract (full-justify words max-width) - (-> (listof string?) exact-integer? (listof string?)) - - (define/contract (justify-line line [last-line #f]) - (->* ((listof string?)) (boolean?) string?) - (define gaps (sub1 (length line))) - (cond [last-line - (~a (string-join line " ") #:min-width max-width)] ; Right-pad the last line - [(= 1 (length line)) - (~a (first line) #:min-width max-width)] ; Right-pad single-word lines - [else - (let* ([words-length (apply + (map string-length line))] - [spacing-length (- max-width words-length)] ; How many spaces do we need? - [spacing-list (make-list gaps 1)] ; Every gap needs to be at least - [distribute (- spacing-length gaps)] ; 1 space long, so we need to - [distributed-spaces ; distribute the excess - (for/list ([space (in-list spacing-list)] - [i (in-naturals 1)]) - (+ space - (quotient distribute gaps) ; Add an equal number of spaces - (if (<= i ; to each gap, then add the - (modulo distribute gaps)) 1 0)))]) ; remainder at the front - (apply string-append - (append (map (λ (w s) - (string-append ; Knit together the first (n-1) - w (make-string s #\space))) ; words and gaps, then append - (drop-right line 1) ; the final word at the end - distributed-spaces) - (take-right line 1))))])) - - (for/fold ([lines '()] ; List of justified lines - [line-acc '()] ; Words to fit into the next line - #:result (append lines ; Only return the list of lines - (list (justify-line line-acc #t)))) ; and append the final line - ([word (in-list words)]) - (let* ([candidate-acc (append line-acc (list word))] - [candidate-length - (string-length (string-join candidate-acc " "))]) - (if (candidate-length . <= . max-width) ; If the word fits into the line, - (values lines ; keep the current line list - candidate-acc) ; and add it to the accumulator - (values (append lines (list (justify-line line-acc))) ; Otherwise, wrap up this line - (list word)))))) ; and start a new accumulator \ No newline at end of file diff --git a/leetcode/lc-690-employee-importance.rkt b/leetcode/lc-690-employee-importance.rkt deleted file mode 100644 index 1fb3fcc..0000000 --- a/leetcode/lc-690-employee-importance.rkt +++ /dev/null @@ -1,14 +0,0 @@ -#lang racket - -(define/contract (sum-even-after-queries A queries) - (-> (listof exact-integer?) - (listof (listof exact-integer?)) - (listof exact-integer?)) - (define array (list->vector A)) - (for/list ([query (in-list queries)]) - (vector-set! array - (second query) - (+ (first query) (vector-ref array (second query)))) - (for/sum ([element (vector-filter even? array)]) element))) - -(sum-even-after-queries '[1 2 3 4] '[[1 0] [-3 1] [-4 0] [2 3]]) \ No newline at end of file diff --git a/leetcode/lc-717-1bit-and-2bit.rkt b/leetcode/lc-717-1bit-and-2bit.rkt deleted file mode 100644 index d9988ec..0000000 --- a/leetcode/lc-717-1bit-and-2bit.rkt +++ /dev/null @@ -1,12 +0,0 @@ -#lang racket - -(define/contract (is-one-bit-character bits) - (-> (listof exact-integer?) boolean?) - (define/match (check-next-character x . xs) - [(0 '()) #true] - [(1 (list _ ..2)) (apply check-next-character (cdr xs))] - [(0 (list _ ..1)) (apply check-next-character xs)] - [(_ _) #false]) - (apply check-next-character bits)) - -(is-one-bit-character (list 1 1 0 1 0)) \ No newline at end of file diff --git a/leetcode/lc-745-prefix-suffix.rkt b/leetcode/lc-745-prefix-suffix.rkt deleted file mode 100644 index b01e3bb..0000000 --- a/leetcode/lc-745-prefix-suffix.rkt +++ /dev/null @@ -1,27 +0,0 @@ -#lang racket -(define word-filter% - (class object% - (super-new) - - ; words : (listof string?) - (init-field - words) ; Take in the provided dictionary. - (define word-ends-hash (make-hash)) ; Make an empty hash table. - - (for/list ([w (in-list words)] ; For each word in the dictionary, - [index (in-naturals)]) ; and its corresponding index, - (define len (string-length w)) ; calculate its length, - (for*/list ([head (in-range 1 (min 11 (add1 len)))] ; and for every combination of head length - [tail (in-range 1 (min 11 (add1 len)))]) ; and tail length - (hash-set! word-ends-hash ; from 1 to the max. affix length, - (list (substring w 0 head) ; set the key to the list containing - (substring w (- len tail) len)) ; the prefix and suffix - index))) ; and map it to the word's index. - - ; f : string? string? -> exact-integer? - (define/public (f prefix suffix) ; Return the mapped value for the affixes - (hash-ref word-ends-hash (list prefix suffix) -1)))) ; or -1 if it doesn't exist. - -;; Your word-filter% object will be instantiated and called as such: -;; (define obj (new word-filter% [words words])) -;; (define param_1 (send obj f prefix suffix)) \ No newline at end of file diff --git a/leetcode/lc-747-largest-number-twice.rkt b/leetcode/lc-747-largest-number-twice.rkt deleted file mode 100644 index cea931b..0000000 --- a/leetcode/lc-747-largest-number-twice.rkt +++ /dev/null @@ -1,15 +0,0 @@ -#lang racket -(define/contract (dominant-index nums) - (-> (listof exact-integer?) exact-integer?) - (if (empty? (cdr nums)) - 0 - (let* ([indexed-list - (map cons nums (range (length nums)))] - [sorted-indexed-list - (sort indexed-list > #:key car)]) - (if ((car (first sorted-indexed-list)) . >= . (* 2 (car (second sorted-indexed-list)))) - (cdr (first sorted-indexed-list)) - -1)))) - -(dominant-index '(3 6 1 0)) -(dominant-index '(0)) \ No newline at end of file diff --git a/leetcode/lc-766-toeplitz-matrix.rkt b/leetcode/lc-766-toeplitz-matrix.rkt deleted file mode 100644 index 5606d2a..0000000 --- a/leetcode/lc-766-toeplitz-matrix.rkt +++ /dev/null @@ -1,9 +0,0 @@ -#lang racket - -(define/contract (is-toeplitz-matrix matrix) - (-> (listof (listof exact-integer?)) boolean?) - (cond [(empty? (cdr matrix)) #true] - [(equal? (drop-right (car matrix) 1) - (drop (cadr matrix) 1)) - (is-toeplitz-matrix (cdr matrix))] - [else #false])) \ No newline at end of file diff --git a/leetcode/lc-771-jewels-and-stones.rkt b/leetcode/lc-771-jewels-and-stones.rkt deleted file mode 100644 index 40ccf14..0000000 --- a/leetcode/lc-771-jewels-and-stones.rkt +++ /dev/null @@ -1,6 +0,0 @@ -#lang racket - -(define/contract (num-jewels-in-stones jewels stones) - (-> string? string? exact-integer?) - (for/sum ([jewel (in-string jewels)]) - (count (curry char=? jewel) (string->list stones)))) \ No newline at end of file diff --git a/leetcode/lc-788-rotated-digits.rkt b/leetcode/lc-788-rotated-digits.rkt deleted file mode 100644 index 79400b8..0000000 --- a/leetcode/lc-788-rotated-digits.rkt +++ /dev/null @@ -1,17 +0,0 @@ -#lang racket - -(define/contract (rotated-digits max-n) - (-> exact-integer? exact-integer?) - (for/fold ([good-number-count 0]) - ([n (in-range 1 (add1 max-n))]) - (if (is-good? n) - (add1 good-number-count) - good-number-count))) - -(define/contract (is-good? test-number) - (-> exact-integer? boolean?) - (define test-string (number->string test-number)) - (match test-string - [(regexp #rx"^[018]*$") #false] - [(regexp #rx"^[0125689]*$") #true] - [_ #false])) \ No newline at end of file diff --git a/leetcode/lc-796-rotate-string.rkt b/leetcode/lc-796-rotate-string.rkt deleted file mode 100644 index b51d9e3..0000000 --- a/leetcode/lc-796-rotate-string.rkt +++ /dev/null @@ -1,6 +0,0 @@ -#lang racket/base -(define/contract (rotate-string A B) - (-> string? string? boolean?) - (define doubled-A (string-append A A)) - (and (= (string-length A) (string-length B)) - (string-contains? doubled-A B))) \ No newline at end of file diff --git a/leetcode/lc-819-most-common-word.rkt b/leetcode/lc-819-most-common-word.rkt deleted file mode 100644 index 68a89c3..0000000 --- a/leetcode/lc-819-most-common-word.rkt +++ /dev/null @@ -1,14 +0,0 @@ -#lang racket -(define/contract (most-common-word paragraph banned) - (-> string? (listof string?) string?) - (define word-count-hash (make-hash)) - (define banned-word-hash - (apply hash (flatten (map (λ (w) (cons w 'banned)) banned)))) - (define word-list - ((compose string-split string-downcase) - (string-replace paragraph #px"[^A-Za-z[:space:]]" " "))) - (for/list ([word (in-list word-list)]) - (cond [(hash-has-key? banned-word-hash word) void] - [else (hash-update! word-count-hash word add1 0)])) - (car (argmax cdr (hash->list word-count-hash)))) - diff --git a/leetcode/lc-836-rectangle-overlap.rkt b/leetcode/lc-836-rectangle-overlap.rkt deleted file mode 100644 index ecdfb56..0000000 --- a/leetcode/lc-836-rectangle-overlap.rkt +++ /dev/null @@ -1,14 +0,0 @@ -#lang racket -(define (rectangle-area lst) - (match-define (list x1 y1 x2 y2) lst) - (* (- x2 x1) (- y2 y1))) - -(define/contract (is-rectangle-overlap rec1 rec2) - (-> (listof exact-integer?) (listof exact-integer?) boolean?) - (cond [(or (= 0 (rectangle-area rec1)) - (= 0 (rectangle-area rec2))) #false] - [(not (or ((list-ref rec1 2) . <= . (list-ref rec2 0)) - ((list-ref rec1 3) . <= . (list-ref rec2 1)) - ((list-ref rec1 0) . >= . (list-ref rec2 2)) - ((list-ref rec1 1) . >= . (list-ref rec2 3)))) #true] - [else #false])) \ No newline at end of file diff --git a/leetcode/lc-844-backspace-string-compare.rkt b/leetcode/lc-844-backspace-string-compare.rkt deleted file mode 100644 index a07ec3c..0000000 --- a/leetcode/lc-844-backspace-string-compare.rkt +++ /dev/null @@ -1,17 +0,0 @@ -#lang racket - -(define/contract (process-backspace-string strng) - (-> string? string?) - (apply ~a (for/fold ([str-out '()] - #:result (reverse str-out)) - ([character (in-string strng)]) - (case character - [(#\#) (if (empty? str-out) - str-out - (cdr str-out))] - [else (cons character str-out)])))) - -(define/contract (backspace-compare s t) - (-> string? string? boolean?) - (equal? (process-backspace-string s) - (process-backspace-string t))) \ No newline at end of file diff --git a/leetcode/lc-896-monotonic-array.rkt b/leetcode/lc-896-monotonic-array.rkt deleted file mode 100644 index fa43244..0000000 --- a/leetcode/lc-896-monotonic-array.rkt +++ /dev/null @@ -1,16 +0,0 @@ -#lang racket -(define/contract (is-monotonic test-list) - (-> (listof exact-integer?) boolean?) - (cond [(empty? (cdr test-list)) #true] - [((car test-list) . > . (cadr test-list)) - (is-monotonic-direction test-list >=)] - [((car test-list) . < . (cadr test-list)) - (is-monotonic-direction test-list <=)] - [else (is-monotonic (cdr test-list))])) - -(define/contract (is-monotonic-direction test-list dir) - (-> (listof exact-integer?) procedure? boolean?) - (cond [(empty? (cdr test-list)) #true] - [((car test-list) . dir . (cadr test-list)) - (is-monotonic-direction (cdr test-list) dir)] - [else #false])) \ No newline at end of file diff --git a/leetcode/lc-9-palindromic-number.rkt b/leetcode/lc-9-palindromic-number.rkt deleted file mode 100644 index 1fccbef..0000000 --- a/leetcode/lc-9-palindromic-number.rkt +++ /dev/null @@ -1,38 +0,0 @@ -#lang racket -(define/contract (is-palindrome x) - (-> exact-integer? boolean?) - ; get the easy cases out of the way first - ; negative numbers are not palindromes, single-digit numbers are - (cond [(x . < . 0) #false] - [(x . < . 10) #true] - [else - ; order-of-magnitude returns the scientific notation exponent - ; so add 1 to get the number of digits - (define digits - (add1 (order-of-magnitude x))) - ; figure out how many digits we need to trim to find the mirrored halves - ; if there are an even number of digits 2n, we will remove n of them from the right - ; if there are an odd number of digits 2n+1, we will remove n+1 of them from the right - (define half-digits - (cond [(even? digits) (/ digits 2)] - [else (/ (add1 digits) 2)])) - ; divide x by a power of 10 to get the digits to match - (define front-half - (quotient x (expt 10 half-digits))) - ; reverse the back half with repeated divisions by 10 - (define back-half-reversed - (for/fold ([reversed 0] - [remaining (remainder x (expt 10 half-digits))] - ; build up the sum of the digits in reversed and return it at the end - #:result reversed) - ; if we have an odd number of digits, we don't need to match the middle one - ([n (in-range (if (even? digits) - half-digits - (sub1 half-digits)))]) - ; shift all the accumulated digits in the mirror to the left one place - ; and add the next one to the right, - ; then chop the right-most digit off the original - (values (+ (* 10 reversed) (remainder remaining 10)) - (quotient remaining 10)))) - ; finally, check to see if the mirrored right is equal to the original left - (= front-half back-half-reversed)])) diff --git a/leetcode/lc-905-sort-by-parity.rkt b/leetcode/lc-905-sort-by-parity.rkt deleted file mode 100644 index 7c736f3..0000000 --- a/leetcode/lc-905-sort-by-parity.rkt +++ /dev/null @@ -1,7 +0,0 @@ -#lang racket -(define/contract (sort-array-by-parity A) - (-> (listof exact-integer?) (listof exact-integer?)) - ((compose flatten (if (odd? (car A)) reverse identity)) - (group-by (λ (n) (modulo n 2)) A))) - -(sort-array-by-parity '(1 1 3 4)) \ No newline at end of file diff --git a/leetcode/lc-944-delete-columns.rkt b/leetcode/lc-944-delete-columns.rkt deleted file mode 100644 index f9714b7..0000000 --- a/leetcode/lc-944-delete-columns.rkt +++ /dev/null @@ -1,7 +0,0 @@ -#lang racket -(define/contract (min-deletion-size strs) - (-> (listof string?) exact-integer?) - (define transposed-strs (apply map list (map string->list strs))) - (count (λ (s) (not (equal? s (sort s char (listof string?) string? boolean?) - (define alpha-order - (make-hash (map (λ (a b) (cons a b)) - (map string (string->list order)) - (build-list (string-length order) identity)))) - (hash-set! alpha-order #\* -1) - (define (alien (listof exact-integer?) exact-integer? (listof exact-integer?)) - (define rev-num (reverse num)) - (define raw-array-form - (cond [(= k 0) num] - [else - (for/fold ([sum-list '()] - [addend k] - [carry 0] - #:result sum-list) - ([place (append rev-num - (make-list (add1 (order-of-magnitude k)) 0))]) - (let* ([place-sum (+ place carry (modulo addend 10))] - [to-carry (quotient place-sum 10)] - [new-place (modulo place-sum 10)]) - (values (cons new-place sum-list) - (quotient addend 10) - to-carry)))])) - (match-define-values - (result _) (drop-common-prefix raw-array-form - (make-list (length raw-array-form) 0))) - (cond [(empty? result) '(0)] - [else result])) \ No newline at end of file diff --git a/leetcode/lc-999-available-captures.rkt b/leetcode/lc-999-available-captures.rkt deleted file mode 100644 index 1b1a3a9..0000000 --- a/leetcode/lc-999-available-captures.rkt +++ /dev/null @@ -1,28 +0,0 @@ -#lang racket -(define/contract (num-rook-captures board) - (-> (listof (listof string?)) exact-integer?) - - (define (get-rook-space [board-state board]) - (for/or ([board-rank (in-list board-state)] - [rank (in-range 0 (length board-state))] - #:when (index-of board-rank "R")) - (list rank (index-of board-rank "R")))) - - (define (check-for-capturable-pawns spaces) - (match spaces - [(list _ ... "p" "." ... "R" "." ... "p" _ ...) 2] - [(list _ ... "R" "." ... "p" _ ...) 1] - [(list _ ... "p" "." ... "R" _ ...) 1] - [_ 0])) - - (define (check-rank rank [board-state board]) - (let ([spaces (list-ref board-state rank)]) - (check-for-capturable-pawns spaces))) - - (define (check-file file [board-state board]) - (let ([spaces (map (curryr list-ref file) board)]) - (check-for-capturable-pawns spaces))) - - (match (get-rook-space board) - [(list rank file) (+ (check-rank rank) - (check-file file))])) \ No newline at end of file diff --git a/racket/aoc2015/day-01/day-01.rkt b/racket/aoc2015/day-01/day-01.rkt new file mode 100644 index 0000000..efbd02a --- /dev/null +++ b/racket/aoc2015/day-01/day-01.rkt @@ -0,0 +1,16 @@ +#lang racket +(require "../../jj-aoc.rkt") + +;; part 1 +(for/fold ([current-floor 0]) ([l (in-input-port-chars (open-day 1 2015))] [i (in-naturals)]) + (match l + [#\( (add1 current-floor)] + [#\) (sub1 current-floor)])) + +;; part 2 +(for/fold ([current-floor 0] [last-index 0] #:result (add1 last-index)) + ([l (in-input-port-chars (open-day 1 2015))] [i (in-naturals)]) + #:break (= current-floor -1) + (match l + [#\( (values (add1 current-floor) i)] + [#\) (values (sub1 current-floor) i)])) diff --git a/racket/aoc2015/day-02/day-02.rkt b/racket/aoc2015/day-02/day-02.rkt new file mode 100644 index 0000000..579fd00 --- /dev/null +++ b/racket/aoc2015/day-02/day-02.rkt @@ -0,0 +1,26 @@ +#lang racket +(require "../../jj-aoc.rkt" + threading + racket/struct) + +(struct present (l w h) #:transparent) + +(define presents + (for/list ([size-string (in-lines (open-day 2 2015))]) + (~> size-string (string-split "x") (map string->number _) (apply present _)))) + +;; part 1 +(define (paper-area p) + (define main-area (~> p struct->list (combinations 2) (map (λ~> (apply * 2 _)) _) (apply + _))) + (define slack-area (~> p struct->list (sort <) (take 2) (apply * _))) + (+ main-area slack-area)) + +(for/sum ([p (in-list presents)]) (paper-area p)) + +;; part 2 +(define (ribbon-length p) + (define ribbon-around-box (~> p struct->list (sort <) (take 2) (map (λ~> (* 2)) _) (apply + _))) + (define ribbon-for-bow (~> p struct->list (apply * _))) + (+ ribbon-around-box ribbon-for-bow)) + +(for/sum ([p (in-list presents)]) (ribbon-length p)) diff --git a/racket/aoc2015/day-03/day-03.rkt b/racket/aoc2015/day-03/day-03.rkt new file mode 100644 index 0000000..1d44955 --- /dev/null +++ b/racket/aoc2015/day-03/day-03.rkt @@ -0,0 +1,28 @@ +#lang racket +(require "../../jj-aoc.rkt" + threading + (only-in algorithms chunks-of) + racket/hash) + +(define directions + (for/list ([l (in-input-port-chars (open-day 3 2015))]) + (string->symbol (string l)))) + +(define (trace-santa dirs) + (define visits (make-hash)) + (for/fold ([x 0] [y 0] #:result visits) ([dir (in-list dirs)]) + (hash-set! visits `(,x ,y) #true) + (match dir + ['^ (values x (add1 y))] + ['v (values x (sub1 y))] + ['< (values (add1 x) y)] + ['> (values (sub1 x) y)]))) + +;; part 1 +(~> directions trace-santa hash-values length) + +;; part 2 +(~> directions (chunks-of 2) (apply map list _) (map trace-santa _) (match-define (list real robo) _)) + +(hash-union! real robo #:combine (λ _ #true)) +(~> real hash-values length) diff --git a/racket/aoc2015/day-04/day-04.rkt b/racket/aoc2015/day-04/day-04.rkt new file mode 100644 index 0000000..2c16043 --- /dev/null +++ b/racket/aoc2015/day-04/day-04.rkt @@ -0,0 +1,18 @@ +#lang racket +(require "../../jj-aoc.rkt" + threading + file/md5) + +(define secret-key (~> (open-day 4 2015) port->string string-trim)) + +(define (find-n-zeroes n) + (for/first ([i (in-naturals)] + #:when + (~>> i (~a secret-key) md5 bytes->string/utf-8 (string-prefix? _ (make-string n #\0)))) + i)) + +;; part 1 +(time (find-n-zeroes 5)) + +;; part 2 +(time (find-n-zeroes 6)) diff --git a/racket/aoc2015/day-05/day-05.rkt b/racket/aoc2015/day-05/day-05.rkt new file mode 100644 index 0000000..3449adc --- /dev/null +++ b/racket/aoc2015/day-05/day-05.rkt @@ -0,0 +1,32 @@ +#lang racket +(require "../../jj-aoc.rkt" + threading) + +(define strs (port->lines (open-day 5 2015))) + +;; part 1 +(define (at-least-three-vowels? str) + (~>> str (regexp-replace* #px"[^aeiou]" _ "") string-length (<= 3))) + +(define (at-least-one-pair? str) + (regexp-match? #px"(.)\\1{1,}" str)) + +(define (no-forbidden-pairs? str) + (~>> (list "ab" "cd" "pq" "xy") (ormap (λ~>> (string-contains? str))) not)) + +(define (nice? str) + (~>> (list at-least-three-vowels? at-least-one-pair? no-forbidden-pairs?) (andmap (λ (f) (f str))))) + +(count nice? strs) + +;; part 2 +(define (repeating-pair? str) + (regexp-match? #px"(..).*\\1" str)) + +(define (symmetry? str) + (regexp-match? #px"(.).\\1" str)) + +(define (new-nice? str) + (~>> (list repeating-pair? symmetry?) (andmap (λ (f) (f str))))) + +(count new-nice? strs) diff --git a/racket/aoc2015/day-06/day-06.rkt b/racket/aoc2015/day-06/day-06.rkt new file mode 100644 index 0000000..d2eed08 --- /dev/null +++ b/racket/aoc2015/day-06/day-06.rkt @@ -0,0 +1,49 @@ +#lang racket +(require "../../jj-aoc.rkt" + threading) + +(struct instruction (todo x1 y1 x2 y2) #:transparent) + +(define (make-instruction lst) + (apply instruction (string->symbol (first lst)) (map string->number (rest lst)))) + +(define instructions + (for/list ([l (in-lines (open-day 6 2015))]) + (~>> l + (regexp-match + #px"(turn on|toggle|turn off) (\\d{1,3}),(\\d{1,3}) through (\\d{1,3}),(\\d{1,3})") + rest + make-instruction))) + +(define (vector2d-modify! vec x y f) + (define pos (+ x (* 1000 y))) + (vector-set! vec pos (f (vector-ref vec pos)))) + +;; part 1 +(define (todo inst) + (match (instruction-todo inst) + ['|turn on| (λ _ #true)] + ['|turn off| (λ _ #false)] + ['|toggle| not])) + +(define (modify-light-grid inst light-grid using) + (for ([x (inclusive-range (instruction-x1 inst) (instruction-x2 inst))]) + (for ([y (inclusive-range (instruction-y1 inst) (instruction-y2 inst))]) + (vector2d-modify! light-grid x y (using inst))))) + +(define light-grid (make-vector (* 1000 1000) #false)) +(for ([i (in-list instructions)]) + (modify-light-grid i light-grid todo)) +(vector-count identity light-grid) + +;; part 2 +(define (todo-dimmer inst) + (match (instruction-todo inst) + ['|turn on| add1] + ['|turn off| (λ (x) (max 0 (sub1 x)))] + ['|toggle| (curry + 2)])) + +(define dimmable-grid (make-vector (* 1000 1000) 0)) +(for ([i (in-list instructions)]) + (modify-light-grid i dimmable-grid todo-dimmer)) +(apply + (vector->list dimmable-grid)) diff --git a/racket/aoc2015/day-25/day-25.rkt b/racket/aoc2015/day-25/day-25.rkt new file mode 100644 index 0000000..975f4c3 --- /dev/null +++ b/racket/aoc2015/day-25/day-25.rkt @@ -0,0 +1,8 @@ +#lang racket + +(define max-r 2978) +(define max-c 3083) + +(for/fold ([code 20151125] [r 1] [c 1]) ([i (in-naturals)] #:break (and (= max-r r) (= max-c c))) + (define new-code (modulo (* code 252533) 33554393)) + (if (= r 1) (values new-code (add1 c) 1) (values new-code (sub1 r) (add1 c)))) diff --git a/racket/aoc2018/day-01/day-01.rkt b/racket/aoc2018/day-01/day-01.rkt new file mode 100644 index 0000000..b18f7c9 --- /dev/null +++ b/racket/aoc2018/day-01/day-01.rkt @@ -0,0 +1,16 @@ +#lang racket + +(require advent-of-code + threading) + +(define deltas + (~>> (open-aoc-input (find-session) 2018 1 #:cache #true) port->lines (map string->number))) + +;; part 1 +(for/sum ([delta deltas]) delta) + +;; part 2 +(for/fold ([seen (set)] [current-frequency 0] #:result current-frequency) ([delta (in-cycle deltas)]) + (define new-frequency (+ current-frequency delta)) + #:final (set-member? seen new-frequency) + (values (set-add seen new-frequency) new-frequency)) diff --git a/racket/aoc2018/day-02/day-02.rkt b/racket/aoc2018/day-02/day-02.rkt new file mode 100644 index 0000000..38155fb --- /dev/null +++ b/racket/aoc2018/day-02/day-02.rkt @@ -0,0 +1,27 @@ +#lang racket + +(require advent-of-code + threading) + +(define ids (port->lines (open-aoc-input (find-session) 2018 2 #:cache #true))) + +;; part 1 +(define (make-baskets str) + (for/fold ([baskets (hash)]) ([ch (in-string str)]) + (hash-update baskets ch add1 0))) + +(define (has-count n ht) + (member n (hash-values ht))) + +(for/fold ([two 0] [three 0] #:result (* two three)) ([id (in-list ids)]) + (define baskets (make-baskets id)) + (values (if (has-count 2 baskets) (add1 two) two) (if (has-count 3 baskets) (add1 three) three))) + +;; part 2 +(define (string-difference str1 str2) + (for/sum ([ch1 (in-string str1)] [ch2 (in-string str2)]) (if (equal? ch1 ch2) 0 1))) + +(for*/first ([id1 (in-list ids)] [id2 (in-list ids)] #:when (= 1 (string-difference id1 id2))) + (~>> (for/list ([ch1 (in-string id1)] [ch2 (in-string id2)] #:when (equal? ch1 ch2)) + ch1) + (apply string))) diff --git a/racket/aoc2018/day-03/day-03.rkt b/racket/aoc2018/day-03/day-03.rkt new file mode 100644 index 0000000..b486361 --- /dev/null +++ b/racket/aoc2018/day-03/day-03.rkt @@ -0,0 +1,51 @@ +#lang racket + +(require advent-of-code + threading + data/applicative + data/monad + megaparsack + megaparsack/text) + +(struct claim (number start-x start-y size-x size-y) #:transparent) + +(define claim/p + (do (char/p #\#) + [number <- integer/p] + (string/p " @ ") + [start-x <- integer/p] + (char/p #\,) + [start-y <- integer/p] + (string/p ": ") + [size-x <- integer/p] + (char/p #\x) + [size-y <- integer/p] + (pure (claim number start-x start-y size-x size-y)))) + +(define (parse-claim str) + (parse-result! (parse-string claim/p str))) + +(define (make-claim ht cl) + (for*/fold ([fabric ht]) + ([x (in-range (claim-start-x cl) (+ (claim-start-x cl) (claim-size-x cl)))] + [y (in-range (claim-start-y cl) (+ (claim-start-y cl) (claim-size-y cl)))]) + (hash-update fabric (cons x y) add1 0))) + +(define claims + (~> (port->lines (open-aoc-input (find-session) 2018 3 #:cache #true)) (map parse-claim _))) + +(define claimed-fabric + (for/fold ([fabric (hash)]) ([cl (in-list claims)]) + (make-claim fabric cl))) + +;; part 1 +(for/sum ([claim-count (in-list (hash-values claimed-fabric))] #:when (< 1 claim-count)) 1) + +;; part 2 +(define (uncontested-claim? fabric cl) + (for*/and ([x (in-range (claim-start-x cl) (+ (claim-start-x cl) (claim-size-x cl)))] + [y (in-range (claim-start-y cl) (+ (claim-start-y cl) (claim-size-y cl)))]) + (= 1 (hash-ref fabric (cons x y))))) + +(for/first ([cl (in-list claims)] #:when (uncontested-claim? claimed-fabric cl)) + (claim-number cl)) diff --git a/racket/aoc2018/day-04/day-04.rkt b/racket/aoc2018/day-04/day-04.rkt new file mode 100644 index 0000000..3660099 --- /dev/null +++ b/racket/aoc2018/day-04/day-04.rkt @@ -0,0 +1,43 @@ +#lang racket + +(require advent-of-code + data/applicative + data/monad + megaparsack + megaparsack/text + threading) + +(struct entry (month day hour minute message) #:transparent) + +(define (parse-message chrs) + (define str (apply string chrs)) + (match str + ["wakes up" 'awake] + ["falls asleep" 'asleep] + [shift (~> shift (string-trim "Guard #") (string-trim " begins shift") string->number)])) + +(define entry/p + (do (string/p "[1518-") + [month <- integer/p] + (char/p #\-) + [day <- integer/p] + space/p + [hour <- integer/p] + (char/p #\:) + [minute <- integer/p] + (string/p "] ") + [message <- (many/p any-char/p)] + (pure (entry month day hour minute (parse-message message))))) + +(define (parse-entry str) + (parse-result! (parse-string entry/p str))) + +(define entries + (~> (port->lines (open-aoc-input (find-session) 2018 4 #:cache #true)) (map parse-entry _))) + +(define sorted-entries + (~> entries + (sort < #:key entry-minute) + (sort < #:key entry-hour) + (sort < #:key entry-day) + (sort < #:key entry-month))) diff --git a/racket/aoc2018/day-05/day-05.rkt b/racket/aoc2018/day-05/day-05.rkt new file mode 100644 index 0000000..a78f5b5 --- /dev/null +++ b/racket/aoc2018/day-05/day-05.rkt @@ -0,0 +1,31 @@ +#lang racket + +(require advent-of-code + threading) + +(define starting-chain + (~> (fetch-aoc-input (find-session) 2018 5 #:cache #true) string-trim string->list)) + +(define (reactive-pair? ch1 ch2) + (and (equal? (char-downcase ch1) (char-downcase ch2)) (not (equal? ch1 ch2)))) + +(define (remove-reactive-pairs chs [acc '()]) + (match chs + [(list* ch1 ch2 rest-chs) + #:when (reactive-pair? ch1 ch2) + (remove-reactive-pairs rest-chs acc)] + [(list* ch rest-chs) (remove-reactive-pairs rest-chs (cons ch acc))] + [(list) (reverse acc)])) + +(define (keep-removing-reactive-pairs chs) + (define chs* (remove-reactive-pairs chs)) + (if (equal? chs chs*) (length chs) (keep-removing-reactive-pairs chs*))) + +;; part 1 +(keep-removing-reactive-pairs starting-chain) + +;; part 2 +(~>> (for/list ([letter (in-string "abcdefghijklmnopqrstuvwxyz")]) + (define tweaked-chain (filter (λ (c) (not (equal? (char-downcase c) letter))) starting-chain)) + (keep-removing-reactive-pairs tweaked-chain)) + (apply min)) diff --git a/racket/aoc2018/day-06/day-06.rkt b/racket/aoc2018/day-06/day-06.rkt new file mode 100644 index 0000000..6f1f7b4 --- /dev/null +++ b/racket/aoc2018/day-06/day-06.rkt @@ -0,0 +1 @@ +#lang racket diff --git a/racket/aoc2019/day-02/day-02.rkt b/racket/aoc2019/day-02/day-02.rkt new file mode 100644 index 0000000..56019e8 --- /dev/null +++ b/racket/aoc2019/day-02/day-02.rkt @@ -0,0 +1,32 @@ +#lang racket + +(require advent-of-code + threading) + +(define initial-opcodes + (parameterize ([current-readtable (make-readtable #f #\, #\space #f)]) + (port->list read (open-aoc-input (find-session) 2019 2 #:cache #true)))) + +;; part 1 +(define (edit-opcode ocs oc-1 oc-2) + (~> ocs (list-set 1 oc-1) (list-set 2 oc-2))) + +(define (run-intcode ocs) + (for/fold ([ocs ocs] #:result (car ocs)) + ([pointer (in-range 0 (length initial-opcodes) 4)] #:break (= 99 (list-ref ocs pointer))) + (define op + (match (list-ref ocs pointer) + [1 +] + [2 *])) + (list-set ocs + (list-ref ocs (+ 3 pointer)) + (op (list-ref ocs (list-ref ocs (+ 1 pointer))) + (list-ref ocs (list-ref ocs (+ 2 pointer))))))) + +(~> initial-opcodes (edit-opcode 12 2) run-intcode) + +;; part 2 +(for*/first ([noun (inclusive-range 0 99)] + [verb (inclusive-range 0 99)] + #:when (~> initial-opcodes (edit-opcode noun verb) run-intcode (= 19690720))) + (+ (* 100 noun) verb)) diff --git a/racket/aoc2019/day-03/day-03.rkt b/racket/aoc2019/day-03/day-03.rkt new file mode 100644 index 0000000..6da3a07 --- /dev/null +++ b/racket/aoc2019/day-03/day-03.rkt @@ -0,0 +1,52 @@ +#lang racket + +(require advent-of-code + threading + fancy-app + racket/hash) + +(define/match (instruction-parse _str) + [((regexp #px"(.)(.+)" (list _ (app string->symbol dir) (app string->number amt)))) (cons dir amt)]) + +(define (wire-parse str) + (~> str (string-split ",") (map instruction-parse _))) + +(define wires + (~>> (fetch-aoc-input (find-session) 2019 3 #:cache #true) string-split (map wire-parse))) + +(define (manhattan-distance-from-origin p) + (+ (abs (car p)) (abs (cdr p)))) + +(define (trace-wire-path wire) + (for/fold ([path (hash)] [x 0] [y 0] [len 0] #:result path) ([inst (in-list wire)]) + (define-values (x* y*) + (match inst + [(cons 'U dy) (values x (+ y dy))] + [(cons 'D dy) (values x (- y dy))] + [(cons 'R dx) (values (+ x dx) y)] + [(cons 'L dx) (values (- x dx) y)])) + (define next-segment + (for*/list ([new-x (inclusive-range x x* (if (< x x*) 1 -1))] + [new-y (inclusive-range y y* (if (< y y*) 1 -1))]) + (cons new-x new-y))) + (define numbered-segments + (for/hash ([segment (in-list next-segment)] [new-len (in-naturals len)]) + (values segment new-len))) + (values (hash-union path numbered-segments #:combine (λ (v0 _) v0)) x* y* (+ len (cdr inst))))) + +;; part 1 +(define wire-paths (map trace-wire-path wires)) + +(define intersections + (~>> wire-paths + (map (λ~> hash-keys list->set)) + (apply set-intersect) + (set-remove _ '(0 . 0)) + set->list)) + +(~>> intersections (map manhattan-distance-from-origin) (apply min)) + +;; part 2 +(~>> (for/list ([intersection (in-list intersections)]) + (~>> wire-paths (map (hash-ref _ intersection)) (apply +))) + (apply min)) diff --git a/racket/aoc2019/day-04/day-04.rkt b/racket/aoc2019/day-04/day-04.rkt new file mode 100644 index 0000000..9518779 --- /dev/null +++ b/racket/aoc2019/day-04/day-04.rkt @@ -0,0 +1,39 @@ +#lang racket + +(define (number->digits n [acc '()]) + (cond + [(< n 10) (cons n acc)] + [else (number->digits (quotient n 10) (cons (remainder n 10) acc))])) + +(define (always-increasing? xs) + (match xs + [(list _) #t] + [(list* a b _) #:when (<= a b) (always-increasing? (cdr xs))] + [_ #f])) + +(define (adjacent-pair? xs) + (match xs + [(list _) #f] + [(list* a a _) a] + [_ (adjacent-pair? (cdr xs))])) + +;; part 1 +(for/sum ([password (inclusive-range 125730 579381)] + #:do [(define digits (number->digits password))] + #:when ((conjoin always-increasing? adjacent-pair?) digits)) + 1) + +;; part 2 +(define (not-in-adjacent-triplet? xs) + (match xs + [(list a a c _ _ _) #:when (not (= a c)) #t] + [(list b a a c _ _) #:when (not (or (= a b) (= a c))) #t] + [(list _ b a a c _) #:when (not (or (= a b) (= a c))) #t] + [(list _ _ b a a c) #:when (not (or (= a b) (= a c))) #t] + [(list _ _ _ b a a) #:when (not (= a b)) #t] + [_ #f])) + +(for/sum ([password (inclusive-range 125730 579381)] + #:do [(define digits (number->digits password))] + #:when ((conjoin always-increasing? adjacent-pair? not-in-adjacent-triplet?) digits)) + 1) \ No newline at end of file diff --git a/racket/aoc2019/day-05/day-05.rkt b/racket/aoc2019/day-05/day-05.rkt new file mode 100644 index 0000000..6f1f7b4 --- /dev/null +++ b/racket/aoc2019/day-05/day-05.rkt @@ -0,0 +1 @@ +#lang racket diff --git a/racket/aoc2020/day-01/day-01.rkt b/racket/aoc2020/day-01/day-01.rkt new file mode 100644 index 0000000..e31c45c --- /dev/null +++ b/racket/aoc2020/day-01/day-01.rkt @@ -0,0 +1,20 @@ +#lang racket +(require "../../jj-aoc.rkt" + threading) + +(define entries (~>> (open-day 01 2020) (port->list read) list->set)) + +;; part 1 +(define (look-for-complement xs) + (define x (set-first xs)) + (cond + [(set-member? xs (- 2020 x)) (* x (- 2020 x))] + [else (look-for-complement (set-rest xs))])) + +(time (look-for-complement entries)) + +;; part 2 +(time (for*/first ([x (in-set entries)] + [y (in-set (set-subtract entries (set x)))] + #:when (set-member? (set-subtract entries (set x y)) (- 2020 x y))) + (* x y (- 2020 x y)))) diff --git a/racket/aoc2020/day-02/day-02.rkt b/racket/aoc2020/day-02/day-02.rkt new file mode 100644 index 0000000..9e22a1a --- /dev/null +++ b/racket/aoc2020/day-02/day-02.rkt @@ -0,0 +1,33 @@ +#lang racket +(require "../../jj-aoc.rkt" + threading) + +(struct policy (least most char pwd) #:transparent) + +(define (make-policy least-str most-str char-str pwd) + (policy (string->number least-str) (string->number most-str) (string-ref char-str 0) pwd)) + +(define policies + (for/list ([l (in-lines (open-day 02 2020))]) + (~>> l (regexp-match #px"(\\d+)-(\\d+) (\\w): (.*)") rest (apply make-policy)))) + +;; part 1 +(define (valid-policy? p) + (~>> p + policy-pwd + string->list + (count (curry char=? (policy-char p))) + ((λ (n) (and (>= n (policy-least p)) (<= n (policy-most p))))))) + +(for/sum ([p (in-list policies)] #:when (valid-policy? p)) 1) + +;; part 2 +(define (valid-revised-policy? p) + (~>> p + policy-pwd + string->list + ((λ (lst) (list (list-ref lst (sub1 (policy-most p))) (list-ref lst (sub1 (policy-least p)))))) + (count (curry char=? (policy-char p))) + (= 1))) + +(for/sum ([p (in-list policies)] #:when (valid-revised-policy? p)) 1) diff --git a/racket/aoc2020/day-03/day-03.rkt b/racket/aoc2020/day-03/day-03.rkt new file mode 100644 index 0000000..ee9edcf --- /dev/null +++ b/racket/aoc2020/day-03/day-03.rkt @@ -0,0 +1,16 @@ +#lang racket +(require "../../jj-aoc.rkt" + threading) + +(define (check-for-trees run rise) + (for*/sum ([(row i) (in-indexed (port->lines (open-day 3 2020)))] + #:when (= 0 (modulo i rise)) + [possible-tree (in-value (sequence-ref (in-cycle row) (* (/ run rise) i)))] + #:when (and (char=? possible-tree #\#))) + 1)) + +;; part 1 +(check-for-trees 3 1) + +;; part 2 +(~>> '((1 1) (3 1) (5 1) (7 1) (1 2)) (map (curry apply check-for-trees)) (apply *)) diff --git a/racket/aoc2020/day-04/day-04.rkt b/racket/aoc2020/day-04/day-04.rkt new file mode 100644 index 0000000..54d50f8 --- /dev/null +++ b/racket/aoc2020/day-04/day-04.rkt @@ -0,0 +1,64 @@ +#lang racket +(require "../../jj-aoc.rkt" + threading) + +(define passports + (~> (open-day 4 2020) (port->string) (string-split "\n\n") (map (λ~> (string-replace "\n" " ")) _))) + +;; part 1 +(define required-fields (list "byr:" "iyr:" "eyr:" "hgt:" "hcl:" "ecl:" "pid:")) + +(define (valid-passport? p) + (andmap (λ (s) (string-contains? p s)) required-fields)) + +(count valid-passport? passports) + +;; part 2 +(define passport-fields + (for/list ([p (in-list passports)] #:when (valid-passport? p)) + (~> p string-split (map (curryr string-split ":") _) flatten (apply hash _)))) + +(define (between x low high) + (and (x . >= . low) (x . <= . high))) + +(define (valid-byr? p) + (define year (string->number (hash-ref p "byr"))) + (between year 1920 2002)) + +(define (valid-iyr? p) + (define year (string->number (hash-ref p "iyr"))) + (between year 2010 2020)) + +(define (valid-eyr? p) + (define year (string->number (hash-ref p "eyr"))) + (between year 2020 2030)) + +(define (valid-hgt? p) + (define height (hash-ref p "hgt")) + (cond + [(string-suffix? height "cm") + (let ([h (string->number (string-trim height "cm"))]) (between h 150 193))] + [(string-suffix? height "in") + (let ([h (string->number (string-trim height "in"))]) (between h 59 76))] + [else #false])) + +(define (valid-hcl? p) + (define color (hash-ref p "hcl")) + (regexp-match #px"^#[0-9a-f]{6}$" color)) + +(define (valid-ecl? p) + (member (hash-ref p "ecl") (list "amb" "blu" "brn" "gry" "grn" "hzl" "oth"))) + +(define (valid-pid? p) + (regexp-match #px"^\\d{9}$" (hash-ref p "pid"))) + +(define (valid-stricter-passport? p) + (and (valid-byr? p) + (valid-iyr? p) + (valid-eyr? p) + (valid-hgt? p) + (valid-hcl? p) + (valid-ecl? p) + (valid-pid? p))) + +(count valid-stricter-passport? passport-fields) diff --git a/racket/aoc2020/day-05/day-05.rkt b/racket/aoc2020/day-05/day-05.rkt new file mode 100644 index 0000000..bd89ede --- /dev/null +++ b/racket/aoc2020/day-05/day-05.rkt @@ -0,0 +1,35 @@ +#lang racket +(require "../../jj-aoc.rkt" + threading) + +(define tickets + (for/list ([l (in-lines (open-day 5 2020))]) + (~>> l (regexp-match #px"(.{7})(.{3})") rest))) + +(define (find-place str min-p max-p l r) + (if (string=? "" str) + min-p + (let ([p-range (/ (add1 (- max-p min-p)) 2)] [c (substring str 0 1)]) + (cond + [(string=? c l) (find-place (substring str 1) min-p (- max-p p-range) l r)] + [(string=? c r) (find-place (substring str 1) (+ min-p p-range) max-p l r)])))) + +(define (find-row str) + (find-place str 0 127 "F" "B")) +(define (find-col str) + (find-place str 0 7 "L" "R")) + +(define (ticket-id t) + (let ([row (find-row (first t))] [col (find-col (second t))]) (+ col (* 8 row)))) + +;; part 1 +(define occupied-seats + (~>> (for/list ([t (in-list tickets)]) + (ticket-id t)))) + +(apply max occupied-seats) + +;; part 2 +(set-first (set-subtract + (list->set (inclusive-range (apply min occupied-seats) (apply max occupied-seats))) + (list->set occupied-seats))) diff --git a/racket/aoc2020/day-06/day-06.rkt b/racket/aoc2020/day-06/day-06.rkt new file mode 100644 index 0000000..b0e2af9 --- /dev/null +++ b/racket/aoc2020/day-06/day-06.rkt @@ -0,0 +1,22 @@ +#lang racket +(require "../../jj-aoc.rkt" + threading) + +(define responses (~> (open-day 6 2020) (port->string) (string-split "\n\n"))) + +;; part 1 +(define (response-count-total rs) + (for/sum ([r (in-list rs)]) (~> r (string-replace _ "\n" "") string->list list->set set-count))) + +(response-count-total responses) + +;; part 2 +(define (response-consensus-total rs) + (for/sum ([r (in-list rs)]) + (~> r + (string-split _ "\n") + (map (λ~> string->list list->set) _) + (apply set-intersect _) + set-count))) + +(response-consensus-total responses) \ No newline at end of file diff --git a/racket/aoc2020/day-07/day-07.rkt b/racket/aoc2020/day-07/day-07.rkt new file mode 100644 index 0000000..f2a1ffe --- /dev/null +++ b/racket/aoc2020/day-07/day-07.rkt @@ -0,0 +1,46 @@ +#lang racket +(require advent-of-code + threading + rebellion/collection/entry + rebellion/collection/multidict) + +(define raw-rules (~> (open-aoc-input (find-session) 2020 7) (port->string) (string-split "\n"))) + +(define (split-rule r) + (match-define (list head tail) (string-split (string-trim r #px" bags?.") " bags contain ")) + (~>> tail + (regexp-split #px"( bags?,\\s)" _) + (map (λ~> (regexp-match* #px"(\\d+) (\\w+ \\w+)" _ #:match-select rest))) + append* + (map (λ~> (match _ + [(list n c) (list (string->symbol c) (string->number n))] + ['() '()]))) + (cons (string->symbol head) _))) + +(define rules-multidict + (for*/multidict ([ln (in-list raw-rules)] #:do [(match-define (list* from tos) (split-rule ln))] + [to (in-list tos)]) + (entry from to))) + +;; part 1 +(define (bags-that-eventually-contain target) + (for/fold ([holders (set)]) ([rule (in-multidict-entries rules-multidict)]) + (match rule + [(entry outside (list (== target) _)) + (set-union (set-add holders outside) (bags-that-eventually-contain outside))] + [_ holders]))) + +(define part-1 (set-count (bags-that-eventually-contain '|shiny gold|))) +(~a "Part 1: " part-1) +;; (aoc-submit (find-session) 2020 7 1 part-1) + +;; part 2 +(define (bags-that-are-contained-by target) + (for/sum ([holding (in-multidict-entries rules-multidict)]) + (match holding + [(entry (== target) (list held n)) (+ n (* n (bags-that-are-contained-by held)))] + [_ 0]))) + +(define part-2 (bags-that-are-contained-by '|shiny gold|)) +(~a "Part 2: " part-2) +;; (aoc-submit (find-session) 2020 7 1 part-2) diff --git a/racket/aoc2020/day-08/day-08.ipynb b/racket/aoc2020/day-08/day-08.ipynb new file mode 100644 index 0000000..1cb060b --- /dev/null +++ b/racket/aoc2020/day-08/day-08.ipynb @@ -0,0 +1,220 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Advent of Code 2020\n", + "#### Day 8: Handheld Halting\n", + "\n", + "A series of instructions consisting of jumps, accumulator increments and no-ops has an infinite loop, but changing one no-op to a jump or vice versa will allow it to run to completion.\n", + "\n", + "1. What's the value of the accumulator immediately before the instructions begin to loop?\n", + "2. After fixing the wrong instruction, what's the value of the accumulator at the end of execution?\n", + "\n", + "No surprises in the preamble, just the usual AOC utility functions and the threading macros." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "#lang iracket/lang #:require racket\n", + "(require advent-of-code\n", + " threading)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The instructions are in a text file that looks like\n", + "```\n", + "nop +0\n", + "acc +1\n", + "jmp +4\n", + "acc +3\n", + "jmp -3\n", + "acc -99\n", + "acc +1\n", + "jmp -4\n", + "acc +6\n", + "```\n", + "Since we need to keep track of which instructions to jump to, I've turned it into a hash table so each instruction is indexed starting at 0." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "'((0 acc . 49) (1 jmp . 274) (2 acc . 49) (3 acc . 49) (4 jmp . 476) (5 jmp . 409) (6 jmp . 269) (7 jmp . 1) (8 acc . -11) (9 acc . 5))" + ], + "text/plain": [ + "'((0 acc . 49) (1 jmp . 274) (2 acc . 49) (3 acc . 49) (4 jmp . 476) (5 jmp . 409) (6 jmp . 269) (7 jmp . 1) (8 acc . -11) (9 acc . 5))" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(define raw-instructions (~> (fetch-aoc-input (find-session) 2020 8) (string-split \"\\n\")))\n", + "\n", + "(define instruction-set\n", + " (for/hash ([instruction (in-list raw-instructions)] [i (in-naturals)])\n", + " (match-define (list op val) (string-split instruction))\n", + " (values i (cons (string->symbol op) (string->number val)))))\n", + "\n", + "(for/list ([i (in-range 10)])\n", + " (cons i (hash-ref instruction-set i)))\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Part 1\n", + "\n", + "Now I write a little interpreter using structural pattern matching and recursion to execute the code.\n", + "* If the program tried to execute an instruction on the line immediately after the last instruction, the program terminates normally. (This won't happen in part 1, but it's important for part 2.)\n", + "* We track the line numbers that have been visited in each step. If a number comes up a second time, that means we're about to start looping, so we break execution here.\n", + "* `acc n` instructions increment the accumulator by `n` and go to the next line.\n", + "* `jmp n` instructions skip up or down `n` instructions.\n", + "* `nop n` instructions don't do anything besides go to the next line." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "'(looping . 1949)" + ], + "text/plain": [ + "'(looping . 1949)" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(define (execute code [acc 0] [line 0] [visited (set)])\n", + " (match (hash-ref code line 'terminated)\n", + " ['terminated (cons 'terminated acc)]\n", + " [_\n", + " #:when (set-member? visited line)\n", + " (cons 'looping acc)]\n", + " [(cons 'acc n) (execute code (+ acc n) (add1 line) (set-add visited line))]\n", + " [(cons 'jmp n) (execute code acc (+ n line) (set-add visited line))]\n", + " [(cons 'nop _) (execute code acc (add1 line) (set-add visited line))]))\n", + "\n", + "(execute instruction-set)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Part 2\n", + "\n", + "So far so good. Now we're told that flipping exactly one `jmp` to a `nop` or vice versa will fix the code, so let's write a utility function to perform that flip, identify the potential candidates for the fix and get an idea of what our workload will be for this problem." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "305" + ], + "text/plain": [ + "305" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(define (flip-op op)\n", + " (match op\n", + " [(cons 'jmp n) (cons 'nop n)]\n", + " [(cons 'nop n) (cons 'jmp n)]))\n", + "\n", + "(define instruction-count (hash-count instruction-set))\n", + "(define flippable-bits\n", + " (filter (λ (i) (member (car (hash-ref instruction-set i)) '(jmp nop))) (range instruction-count)))\n", + "\n", + "(length flippable-bits)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "There's only 305 cases to check, so just starting from the top and trying each possible swap in sequence should work fine, rather than trying to come up with some fancier backtracking algorithm. `for/or` stops at the first non-falsy result, so we just have to wait for the first result that matches the `(cons 'terminated _)` pattern." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "'(terminated . 2092)" + ], + "text/plain": [ + "'(terminated . 2092)" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(for/or ([i (in-list flippable-bits)])\n", + " (define flipped-instruction-set (hash-update instruction-set i flip-op))\n", + " (match (execute flipped-instruction-set)\n", + " [(cons 'looping _) #f]\n", + " [(and success (cons 'terminated _)) success]))\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Racket (trusted)", + "language": "racket", + "name": "racket-trusted" + }, + "language_info": { + "codemirror_mode": "scheme", + "file_extension": ".rkt", + "mimetype": "text/x-racket", + "name": "racket", + "pygments_lexer": "racket", + "version": "8.7" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/racket/aoc2020/day-09/day-09.ipynb b/racket/aoc2020/day-09/day-09.ipynb new file mode 100644 index 0000000..e6f712b --- /dev/null +++ b/racket/aoc2020/day-09/day-09.ipynb @@ -0,0 +1,166 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Advent of Code 2020\n", + "#### Day 9: Encoding Error\n", + "\n", + "In a list of integers, each number after the 25th should be the sum of two of the previous 25 numbers.\n", + "\n", + "1. What's the first number in the list that does not have this property?\n", + "2. The \"encryption weakness\" is the sum of the extrema in a contiguous range of numbers that sums up to the invalid number in part 1. Find the encryption weakness.\n", + "\n", + "I'm using structural pattern matching for this solution, so no extra packages are required beyond the usual." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "vscode": { + "languageId": "racket" + } + }, + "outputs": [], + "source": [ + "#lang iracket/lang #:require racket\n", + "\n", + "(require advent-of-code\n", + " threading)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The data is just a list of integers, so it's straightforward to process." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "vscode": { + "languageId": "racket" + } + }, + "outputs": [], + "source": [ + "(define preamble\n", + " (~> (fetch-aoc-input (find-session) 2020 9) (string-split \"\\n\") (map string->number _)))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### Part 1\n", + "\n", + "First, we bind the first 25 values to `head` and the 26th to `x`.\n", + "\n", + "In the `match` syntax, `list-no-order` binds `a` and `b` to the first pair of numbers from anywhere in the first 25 values that satisfies the `#:when` guard. The exact pair doesn't matter; all we need to know is if it's valid and we can move on to the next test.\n", + "\n", + "If nothing satisfies the first clause, we've found our invalid number. We're guaranteed to have an invalid number in the set, so we don't need to guard against `match-define-values` failing when there's fewer than 26 values to work with." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "vscode": { + "languageId": "racket" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "1038347917" + ], + "text/plain": [ + "1038347917" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(define (find-invalid-number xs)\n", + " (match-define-values (head (list x _ ...)) (split-at xs 25))\n", + " (match head\n", + " [(list-no-order a b _ ...)\n", + " #:when (= x (+ a b))\n", + " (find-invalid-number (rest xs))]\n", + " [_ x]))\n", + "\n", + "(define target-sum (find-invalid-number preamble))\n", + "target-sum" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### Part 2\n", + "\n", + "We can find the range with another match statement, this time looking for a sub-list that's at least two elements long and that satisfies the guard. Everything after this is just arithmetic." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "vscode": { + "languageId": "racket" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "137394018" + ], + "text/plain": [ + "137394018" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(define (find-contiguous-range xs)\n", + " (match xs\n", + " [(list _ ... x ..2 _ ...)\n", + " #:when (= (apply + x) target-sum)\n", + " x]))\n", + "\n", + "(define target-range (find-contiguous-range preamble))\n", + "(+ (apply max target-range) (apply min target-range))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Racket (trusted)", + "language": "racket", + "name": "racket-trusted" + }, + "language_info": { + "codemirror_mode": "scheme", + "file_extension": ".rkt", + "mimetype": "text/x-racket", + "name": "Racket", + "pygments_lexer": "racket", + "version": "8.7" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/racket/aoc2020/day-10/day-10.rkt b/racket/aoc2020/day-10/day-10.rkt new file mode 100644 index 0000000..77d9bb7 --- /dev/null +++ b/racket/aoc2020/day-10/day-10.rkt @@ -0,0 +1,37 @@ +#lang racket + +(require advent-of-code + threading + algorithms + memoize) + +;; part 1 +(define adapters + (~> (fetch-aoc-input (find-session) 2020 10) + (string-split "\n") + (map string->number _) + (sort <) + ((λ (xs) (flatten (list 0 xs (+ 3 (last xs)))))))) + +(~>> adapters + (sliding _ 2) + (map (match-lambda + [(list b a) (- a b)])) + (group-by identity) + (map length) + (apply *)) + +;; part 2 +(define subpaths + (for*/hash ([adapter (in-list adapters)]) + (define predecessor-candidates (inclusive-range (+ 1 adapter) (+ 3 adapter))) + (values adapter (filter (λ (p) (member p adapters)) predecessor-candidates)))) + +(define/memo (find-paths from to) + (define paths (hash-ref subpaths from 'failed)) + (match paths + ['failed 0] + [(list-no-order (== to) _ ...) 1] + [ts (for/sum ([t (in-list ts)]) (find-paths t to))])) + +(find-paths (first adapters) (last adapters)) diff --git a/racket/aoc2020/day-11/day-11.rkt b/racket/aoc2020/day-11/day-11.rkt new file mode 100644 index 0000000..e2fe052 --- /dev/null +++ b/racket/aoc2020/day-11/day-11.rkt @@ -0,0 +1,60 @@ +#lang racket + +(require advent-of-code) + +(define raw-grid (fetch-aoc-input (find-session) 2020 11)) + +(define/match (parse _) + [(#\L) 'empty] + [(#\#) 'occupied] + [(#\.) 'floor]) + +(define seat-grid + (for*/hash ([(row r) (in-indexed (in-list (string-split raw-grid)))] + [(col c) (in-indexed (in-string row))]) + (values (cons r c) (parse col)))) + +(define (next-seat-state seat state h rule [max-occupy 4]) + (define neighbor-states (rule seat h)) + (match* (state (count (curry eq? 'occupied) neighbor-states)) + [('empty 0) 'occupied] + [('occupied n) + #:when (>= n max-occupy) + 'empty] + [(_ _) state])) + +(define (stabilize h [i 1] #:rule rule #:max-occupy [max-occupy 4]) + (define h* + (for/hash ([(seat state) (in-hash h)]) + (cond + [(eq? state 'floor) (values seat state)] + [else (values seat (next-seat-state seat state h rule max-occupy))]))) + (if (equal? h h*) + (count (curry equal? 'occupied) (hash-values h)) + (stabilize h* (add1 i) #:rule rule #:max-occupy max-occupy))) + +;; part 1 +(define (find-nearest-neighbors p h) + (match-define (cons r c) p) + (for*/list ([r* (in-inclusive-range (sub1 r) (add1 r))] + [c* (in-inclusive-range (sub1 c) (add1 c))] + [p* (in-value (cons r* c*))] + #:unless (equal? p p*)) + (hash-ref h p* 'out-of-bounds))) + +(stabilize seat-grid #:rule find-nearest-neighbors) + +;; part 2 +(define (find-visible-neighbors p h) + (match-define (cons r c) p) + (define directions + (for*/list ([dr '(-1 0 1)] [dc '(-1 0 1)] #:unless (= 0 dr dc)) + (cons dr dc))) + (for/list ([dir (in-list directions)] #:do [(match-define (cons dr dc) dir)]) + (for*/first ([i (in-naturals 1)] + #:do [(define p* (cons (+ r (* i dr)) (+ c (* i dc)))) + (define state (hash-ref h p* 'out-of-bounds))] + #:unless (equal? state 'floor)) + state))) + +(stabilize seat-grid #:rule find-visible-neighbors #:max-occupy 5) diff --git a/racket/aoc2020/day-12/day-12.rkt b/racket/aoc2020/day-12/day-12.rkt new file mode 100644 index 0000000..e4bbd32 --- /dev/null +++ b/racket/aoc2020/day-12/day-12.rkt @@ -0,0 +1,56 @@ +#lang rackjure + +(require advent-of-code + threading + (only-in relation ->symbol ->number)) + +(struct instruction (direction distance) #:transparent) + +(define (parse-instruction str) + (match str + [(regexp #px"(\\w)(\\d+)" (list _ dir dist)) (instruction (->symbol dir) (->number dist))])) + +(define instructions + (~>> (fetch-aoc-input (find-session) 2020 12) string-split (map parse-instruction))) + +;; part 1 +(struct boat (x y nav) #:transparent) + +(define (angle->direction n) + (case n + [(0) 'E] + [(90) 'N] + [(180) 'W] + [(270) 'S])) + +(define (move-via-direct-command inst b) + (match-define (boat x y facing) b) + (match inst + [(instruction 'N n) (boat x (+ y n) facing)] + [(instruction 'S n) (boat x (- y n) facing)] + [(instruction 'E n) (boat (+ x n) y facing)] + [(instruction 'W n) (boat (- x n) y facing)] + [(instruction 'L n) (boat x y (modulo (+ facing n) 360))] + [(instruction 'R n) (boat x y (modulo (- facing n) 360))] + [(instruction 'F n) (move-via-direct-command (instruction (angle->direction facing) n) b)])) + +(define (find-boat-destination using start instructions) + (match-define (boat x y _) (foldl using start instructions)) + (+ (abs x) (abs y))) + +(find-boat-destination move-via-direct-command (boat 0 0 0) instructions) + +;; part 2 +(define (move-via-waypoint inst b) + (match-define (boat x y (cons wp-x wp-y)) b) + (match inst + [(instruction 'N n) (boat x y (cons wp-x (+ wp-y n)))] + [(instruction 'S n) (boat x y (cons wp-x (- wp-y n)))] + [(instruction 'E n) (boat x y (cons (+ wp-x n) wp-y))] + [(instruction 'W n) (boat x y (cons (- wp-x n) wp-y))] + [(instruction _ 180) (boat x y (cons (- wp-x) (- wp-y)))] + [(or (instruction 'L 90) (instruction 'R 270)) (boat x y (cons (- wp-y) wp-x))] + [(or (instruction 'R 90) (instruction 'L 270)) (boat x y (cons wp-y (- wp-x)))] + [(instruction 'F n) (boat (+ x (* n wp-x)) (+ y (* n wp-y)) (cons wp-x wp-y))])) + +(find-boat-destination move-via-waypoint (boat 0 0 '(10 . 1)) instructions) diff --git a/racket/aoc2020/day-13/day-13.rkt b/racket/aoc2020/day-13/day-13.rkt new file mode 100644 index 0000000..b53f045 --- /dev/null +++ b/racket/aoc2020/day-13/day-13.rkt @@ -0,0 +1,32 @@ +#lang racket + +(require advent-of-code + (only-in relation ->number) + threading) + +(define (process-ids str) + (~> str (string-split ",") (filter-map (λ (s) (string->number s 10 'number-or-false)) _))) + +(match-define (regexp #px"(\\d+)\n(.+)" (list _ (app ->number timestamp) raw-bus-ids)) + (fetch-aoc-input (find-session) 2020 13)) + +(define bus-ids (process-ids raw-bus-ids)) + +;; part 1 +(for/first ([minute (in-naturals timestamp)] + #:do [(define departing-bus + (for/first ([b bus-ids] #:when (= 0 (remainder minute b))) + b))] + #:when departing-bus) + (* departing-bus (- minute timestamp))) + +;; part 2 +(for/fold ([step 1] [current-timestamp 1] #:result current-timestamp) + ([b* (in-list (string-split (string-trim raw-bus-ids) ","))] + [offset (in-naturals)] + #:unless (equal? b* "x") + #:do [(define bus (->number b*))]) + (values + (* step bus) + (for/first ([n (in-range current-timestamp +inf.0 step)] #:when (= 0 (remainder (+ n offset) bus))) + n))) diff --git a/racket/aoc2020/day-14/day-14.rkt b/racket/aoc2020/day-14/day-14.rkt new file mode 100644 index 0000000..9ac339c --- /dev/null +++ b/racket/aoc2020/day-14/day-14.rkt @@ -0,0 +1,11 @@ +#lang racket + +(require advent-of-code + threading + fancy-app + relation + rebellion/binary/bitstring) + +(define instructions (string-split (fetch-aoc-input (find-session) 2020 14) "\n")) + +(~> (number->string 11 2) ->list (map (->number _) _)) diff --git a/racket/aoc2020/day-15/day-15.rkt b/racket/aoc2020/day-15/day-15.rkt new file mode 100644 index 0000000..4dd9e88 --- /dev/null +++ b/racket/aoc2020/day-15/day-15.rkt @@ -0,0 +1,22 @@ +#lang rackjure + +(define first-numbers '(2 20 0 4 1 17)) + +(define number-hash + (for/hash ([(xs i) (in-indexed (drop-right first-numbers 1))]) + (values xs (add1 i)))) + +(define starting-round (~> number-hash hash-values (apply max _) (+ 2))) + +(define (find-spoken-number-at end) + (for/fold ([ns number-hash] [previous-number (last first-numbers)] #:result previous-number) + ([rnd (inclusive-range starting-round end)]) + (define next-spoken-number + (match (ns previous-number) + [#f 0] + [n (- (sub1 rnd) n)])) + (values (ns previous-number (sub1 rnd)) next-spoken-number))) + +(find-spoken-number-at 2020) + +(find-spoken-number-at 30000000) \ No newline at end of file diff --git a/racket/aoc2020/day-16/day-16.rkt b/racket/aoc2020/day-16/day-16.rkt new file mode 100644 index 0000000..9a38eda --- /dev/null +++ b/racket/aoc2020/day-16/day-16.rkt @@ -0,0 +1,52 @@ +#lang racket + +(require racket/struct + advent-of-code + fancy-app + relation + threading + rebellion/base/range) + +(struct field-rule (name range1 range2) #:transparent) + +(define (make-lines strs) + (string-split strs "\n")) +(define (seperate-fields strs) + (~>> (string-split strs ",") (map ->number))) + +(define (process-rules str) + (match str + [(regexp + #px"(.+): (\\d+)-(\\d+) or (\\d+)-(\\d+)" + (list _ name (app ->number min1) (app ->number max1) (app ->number min2) (app ->number max2))) + (field-rule name (closed-range min1 max1) (closed-range min2 max2))])) + +(match-define (list (app (λ~>> make-lines (map process-rules)) ticket-rules) + (app (λ~>> make-lines second seperate-fields) your-ticket) + (app (λ~>> make-lines rest (map seperate-fields)) other-tickets)) + (~> (fetch-aoc-input (find-session) 2020 16 #:cache #true) (string-split "\n\n"))) + +;; part 1 +(define (fails-all-checks? field rules) + (define rule-list (~>> rules (map (λ~> struct->list rest)) flatten)) + (for/and ([rule (in-list rule-list)]) + (not (range-contains? rule field)))) + +(define (ticket-scanning-error-rate tickets rules) + (for*/sum + ([ticket (in-list tickets)] (field (in-list ticket)) #:when (fails-all-checks? field rules)) + field)) + +(ticket-scanning-error-rate other-tickets ticket-rules) + +;; part 2 +(define valid-tickets (filter (ormap (fails-all-checks? _ ticket-rules) _) other-tickets)) + +(define fields (apply map list valid-tickets)) + +(for/list ([field (in-list fields)]) + (for*/list ( + [rule (in-list ticket-rules)] + #:unless (not (or (range-contains? (field-rule-range1 rule) value) + (range-contains? (field-rule-range2 rule) value)))) + (field-rule-name rule))) diff --git a/racket/aoc2021/day-01/day-01.pl b/racket/aoc2021/day-01/day-01.pl new file mode 100644 index 0000000..d3c3fa7 --- /dev/null +++ b/racket/aoc2021/day-01/day-01.pl @@ -0,0 +1,20 @@ +:- use_module(library(yall)). +:- use_module(library(apply)). + +get_data(Result) :- + setup_call_cleanup(open("day-01/input.txt", read, In), + (read_string(In, _, Str), + split_string(Str, "\n", "\s\t\n", Lines), + maplist(number_string, Result, Lines)), + close(In)). + +calculate_diffs(Result, WindowWidth) :- + get_data(Xs), + length(TrimLeft, WindowWidth), append(TrimLeft, RightSide, Xs), + length(TrimRight, WindowWidth), append(LeftSide, TrimRight, Xs), + maplist([X, Y, Z]>>(Z is Y - X), LeftSide, RightSide, Diffs), + include([X]>>(X > 0), Diffs, Increases), + length(Increases, Result). + +part1_answer(Result) :- calculate_diffs(Result, 1). +part2_answer(Result) :- calculate_diffs(Result, 3). \ No newline at end of file diff --git a/racket/aoc2021/day-01/day-01.rkt b/racket/aoc2021/day-01/day-01.rkt new file mode 100644 index 0000000..48ef158 --- /dev/null +++ b/racket/aoc2021/day-01/day-01.rkt @@ -0,0 +1,20 @@ +#lang racket +(require advent-of-code + threading) + +;; part 1 +(define sensor-data + (~> (open-aoc-input (find-session) 2021 1 #:cache (string->path "./cache")) + (port->list read _))) + +(define (count-increases data offset) + (for/sum ([x (in-list data)] + [y (in-list (drop data offset))] + #:when (< x y)) + 1)) + +(~a "Part 1: " (count-increases sensor-data 1)) + +;; part 2 + +(~a "Part 2: " (count-increases sensor-data 3)) \ No newline at end of file diff --git a/racket/aoc2021/day-02/day-02.ex b/racket/aoc2021/day-02/day-02.ex new file mode 100644 index 0000000..d37ab05 --- /dev/null +++ b/racket/aoc2021/day-02/day-02.ex @@ -0,0 +1,32 @@ +defmodule Day02 do + def part_one(data) do + data + |> Enum.reduce(%{pos: 0, dep: 0}, &method_one/2) + |> get_answer() + end + + def part_two(data) do + data + |> Enum.reduce(%{pos: 0, dep: 0, aim: 0}, &method_two/2) + |> get_answer() + end + + defp method_one({:forward, x}, s), do: %{s | pos: s.pos + x} + defp method_one({:up, x}, s), do: %{s | dep: s.dep - x} + defp method_one({:down, x}, s), do: %{s | dep: s.dep + x} + + defp method_two({:forward, x}, s), do: %{s | pos: s.pos + x, dep: s.dep + s.aim * x} + defp method_two({:up, x}, s), do: %{s | aim: s.aim - x} + defp method_two({:down, x}, s), do: %{s | aim: s.aim + x} + + defp get_answer(s), do: s.pos * s.dep +end + +data = + File.read!("day-02/input.txt") + |> String.split("\n", trim: true) + |> Enum.map(&String.split/1) + |> Enum.map(fn [dir, amt] -> {String.to_atom(dir), String.to_integer(amt)} end) + +Day02.part_one(data) |> IO.inspect() +Day02.part_two(data) |> IO.inspect() diff --git a/racket/aoc2021/day-02/day-02.rkt b/racket/aoc2021/day-02/day-02.rkt new file mode 100644 index 0000000..0bd0c3d --- /dev/null +++ b/racket/aoc2021/day-02/day-02.rkt @@ -0,0 +1,24 @@ +#lang racket +(require advent-of-code + threading + algorithms) + +(define motion-data + (~> (open-aoc-input (find-session) 2021 2 #:cache (string->path "./cache")) + (port->list read _) + (chunks-of _ 2))) + +;; part 1 +(for/fold ([depth 0] [position 0] #:result (* depth position)) ([motion (in-list motion-data)]) + (match motion + [(list 'forward x) (values depth (+ position x))] + [(list 'up x) (values (- depth x) position)] + [(list 'down x) (values (+ depth x) position)])) + +;; part 2 +(for/fold ([aim 0] [depth 0] [position 0] #:result (* depth position)) + ([motion (in-list motion-data)]) + (match motion + [(list 'forward x) (values aim (+ depth (* aim x)) (+ position x))] + [(list 'up x) (values (- aim x) depth position)] + [(list 'down x) (values (+ aim x) depth position)])) diff --git a/racket/aoc2021/day-03/day-03.rkt b/racket/aoc2021/day-03/day-03.rkt new file mode 100644 index 0000000..95b7efd --- /dev/null +++ b/racket/aoc2021/day-03/day-03.rkt @@ -0,0 +1,39 @@ +#lang racket +(require advent-of-code + threading) + +(define data + (~> (open-aoc-input (find-session) 2021 3 #:cache (string->path "./cache")) + port->lines + (map string->list _))) + +;; part 1 +(define most-common-bits + (for*/list ([row (in-list (apply map list data))] [len (in-value (length data))]) + (if (> (count (λ (c) (char=? #\1 c)) row) (/ len 2)) #\1 #\0))) +(define (bit-list->number lst) + (~> lst (apply string _) (string->number _ 2))) + +(define gamma (bit-list->number most-common-bits)) +(define epsilon (~> most-common-bits (map (λ (c) (if (char=? c #\1) #\0 #\1)) _) bit-list->number)) + +(* gamma epsilon) + +;; part 2 +(define (rating-search data comparison) + (for/fold ([candidates data] #:result (bit-list->number (first candidates))) + ([bit (in-list most-common-bits)] [bit-index (in-range 0 (length most-common-bits))]) + #:break (= 1 (length candidates)) + (define keep-bit + (~> candidates + (apply map list _) + (list-ref _ bit-index) + (count (λ (c) (char=? #\1 c)) _) + (comparison _ (/ (length candidates) 2)) + (if _ #\1 #\0))) + (filter (λ (row) (char=? keep-bit (list-ref row bit-index))) candidates))) + +(define oxygen-rating (rating-search data >=)) +(define scrubber-rating (rating-search data <)) + +(* oxygen-rating scrubber-rating) diff --git a/racket/aoc2021/day-04/day-04.rkt b/racket/aoc2021/day-04/day-04.rkt new file mode 100644 index 0000000..c572f74 --- /dev/null +++ b/racket/aoc2021/day-04/day-04.rkt @@ -0,0 +1,51 @@ +#lang racket +(require advent-of-code + threading + (only-in algorithms chunks-of)) + +(define data + (for/list ([l (in-lines (open-aoc-input (find-session) 2021 4 #:cache (string->path "./cache")))] + #:unless (equal? l "")) + l)) + +(define call-sheet (~> data car (string-split ",") (map string->number _))) +(define bingo-cards + (~> data cdr (map string-split _) (map (λ (row) (map string->number row)) _) (chunks-of 5))) + +(define test-card (first bingo-cards)) + +(define (mark-card card call) + (for/list ([row (in-list card)]) + (for/list ([col (in-list row)]) + (if (eq? col call) 'X col)))) + +(define (check-card card) + (for/or ([row (in-sequences card (apply map list card))]) + (equal? row '(X X X X X)))) + +(define (multiply-by-last-call n call) + (match n + ['X 0] + [n (* n call)])) + +(define (evaluate-cards cards calls [check (curry ormap check-card)] [exception not]) + (for/fold ([current-cards cards] + [last-call 0] + #:result (~> current-cards + (findf check-card _) + flatten + (map (λ (n) (multiply-by-last-call n last-call)) _) + (apply + _))) + ([call (in-list calls)]) + #:break (check current-cards) + (values (for/list ([card (in-list current-cards)] #:unless (exception card)) + (mark-card card call)) + call))) + +;; part 1 +(evaluate-cards bingo-cards call-sheet) +;; part 2 +(evaluate-cards bingo-cards + call-sheet + (λ (cards) (and (= (length cards) 1) (check-card (first cards)))) + check-card) diff --git a/racket/aoc2021/day-05/day-05.rkt b/racket/aoc2021/day-05/day-05.rkt new file mode 100644 index 0000000..e568490 --- /dev/null +++ b/racket/aoc2021/day-05/day-05.rkt @@ -0,0 +1,57 @@ +#lang racket +(require advent-of-code + threading) + +(define data + (for/list ([l (in-lines (open-aoc-input (find-session) 2021 5 #:cache (string->path "./cache")))]) + (~> l (string-replace " -> " ",") (string-split ",") (map string->number _)))) + +(define (trace-line! x y vec) + (define linear-coord (+ y (* 1000 x))) + (vector-set! vec linear-coord (+ 1 (vector-ref vec linear-coord)))) + +(define/match (orthogonal? coord-pair) + [((or (list n _ n _) (list _ n _ n))) #t] + [(_) #f]) + +(define-values (orthogonal-lines diagonal-lines) (partition orthogonal? data)) + +(define (dir a b) + (if (< a b) 1 -1)) + +(define (trace-orthogonal! coord-pairs tracer result) + (for ([coord-pair (in-list coord-pairs)]) + (match coord-pair + [(list x y1 x y2) + (for ([y (inclusive-range y1 y2 (dir y1 y2))]) + (tracer x y result))] + [(list x1 y x2 y) + (for ([x (inclusive-range x1 x2 (dir x1 x2))]) + (tracer x y result))]))) + +(define (trace-diagonal! coord-pairs tracer result) + (for ([coord-pair (in-list coord-pairs)]) + (match-define (list x1 y1 x2 y2) coord-pair) + (for ([x (inclusive-range x1 x2 (dir x1 x2))] [y (inclusive-range y1 y2 (dir y1 y2))]) + (tracer x y result)))) + +;; part 1 +(define sea-floor (make-vector 1000000)) +(trace-orthogonal! orthogonal-lines trace-line! sea-floor) +(vector-count (curry <= 2) sea-floor) + +;; part 2 +;; since the orthogonal lines have already been traced, +;; all I need to do is add the diagonal ones to the existing vector +(trace-diagonal! diagonal-lines trace-line! sea-floor) +(vector-count (curry <= 2) sea-floor) + +;; alternate sparse representation +(define (trace-line-sparse! x y dict) + (hash-update! dict (list x y) (curry + 1) 0)) + +(define sea-floor-dict (make-hash)) +(trace-orthogonal! orthogonal-lines trace-line-sparse! sea-floor-dict) +(count (curry <= 2) (hash-values sea-floor-dict)) +(trace-diagonal! diagonal-lines trace-line-sparse! sea-floor-dict) +(count (curry <= 2) (hash-values sea-floor-dict)) diff --git a/racket/aoc2021/day-06/day-06.ex b/racket/aoc2021/day-06/day-06.ex new file mode 100644 index 0000000..efe10e4 --- /dev/null +++ b/racket/aoc2021/day-06/day-06.ex @@ -0,0 +1,35 @@ +defmodule Day06 do + def next_day(state) do + with one_day_older <- Enum.into(state, %{}, fn {k, v} -> {k - 1, v} end), + {n, s} <- Map.pop(one_day_older, -1, 0) do + Map.update(s, 6, n, &(&1 + n)) + |> Map.put(8, n) + end + end +end + +school = + with {:ok, data} <- File.read("input.txt") do + data + |> String.trim() + |> String.split(",") + |> Enum.map(&String.to_integer/1) + end + +starting_state = Enum.frequencies(school) + +Enum.reduce( + Enum.to_list(1..80), + starting_state, + fn _, acc -> Day06.next_day(acc) end +) +|> Enum.reduce(0, fn {_, v}, acc -> v + acc end) +|> IO.inspect() + +Enum.reduce( + Enum.to_list(1..256), + starting_state, + fn _, acc -> Day06.next_day(acc) end +) +|> Enum.reduce(0, fn {_, v}, acc -> v + acc end) +|> IO.inspect() diff --git a/racket/aoc2021/day-06/day-06.livemd b/racket/aoc2021/day-06/day-06.livemd new file mode 100644 index 0000000..5ab794f --- /dev/null +++ b/racket/aoc2021/day-06/day-06.livemd @@ -0,0 +1,152 @@ + + + +# Advent of Code 2021, Day 6 + +## Short problem summary + +A school of fish reproduce according to the following rules: + +* Every fish has an "internal timer" +* The timer decrements by one every day +* If the timer is at 0, the timer is instead reset to 6, + and a new fish with an internal timer of 8 is added to the school + +Questions: + +1. How many fish are in the school after 80 days? +2. How many fish are in the school after 256 days? + +## Setting up + +The initial input is a list of fish, represented by the initial value of their internal timer: + +```elixir +school = + with {:ok, data} <- File.read("day-06/input.txt") do + data + |> String.trim() + |> String.split(",") + |> Enum.map(&String.to_integer/1) + end +``` + +```output +[5, 4, 3, 5, 1, 1, 2, 1, 2, 1, 3, 2, 3, 4, 5, 1, 2, 4, 3, 2, 5, 1, 4, 2, 1, 1, 2, 5, 4, 4, 4, 1, 5, + 4, 5, 2, 1, 2, 5, 5, 4, 1, 3, 1, 4, 2, 4, 2, 5, 1, ...] +``` + +Every fish with the same starting internal timer will reproduce at the same time, +as will all of the children of those fish and their children, and so forth, +so we don't need to track individual fish; we just need to group the fish based on +their starting internal timer and track those groups throughout the simulation. + +```elixir +starting_state = Enum.frequencies(school) +``` + +```output +%{1 => 88, 2 => 45, 3 => 54, 4 => 52, 5 => 61} +``` + +Every time a day passes, the following things happen: + +* All the internal timers decrement by 1 +* The group of fish with an internal timer of -1 is reset to 6 + (added to any existing fish whose timers are already at 6), + and an equal-sized group of fish with internal timer 8 is added + +```elixir +defmodule Day06 do + def next_day(state) do + with one_day_older <- Enum.into(state, %{}, fn {k, v} -> {k - 1, v} end), + {n, s} <- Map.pop(one_day_older, -1, 0) do + Map.update(s, 6, n, &(&1 + n)) + |> Map.put(8, n) + end + end +end + +day1 = Day06.next_day(starting_state) +``` + +```output +%{0 => 88, 1 => 45, 2 => 54, 3 => 52, 4 => 61, 6 => 0, 8 => 0} +``` + +After the first day there's not any fish old enough to reproduce yet, but after the second day, + +```elixir +day2 = Day06.next_day(day1) +``` + +```output +%{0 => 45, 1 => 54, 2 => 52, 3 => 61, 5 => 0, 6 => 88, 7 => 0, 8 => 88} +``` + +The 88 fish whose timers were at 0 have rolled over to 6 and created 88 more fish with timers at 8. + +## Solution + +Now we just need to apply the transformation function the necessary number +of times and sum up the total population in the end: + +```elixir +part1_state = + Enum.reduce( + Enum.to_list(1..80), + starting_state, + fn _, acc -> Day06.next_day(acc) end + ) + |> IO.inspect() + |> Enum.reduce(0, fn {_, v}, acc -> v + acc end) +``` + +```output +%{ + 0 => 24572, + 1 => 43660, + 2 => 30525, + 3 => 48458, + 4 => 41318, + 5 => 47697, + 6 => 57731, + 7 => 23218, + 8 => 33738 +} +``` + +```output +350917 +``` + +Identically for part 2, + +```elixir +part2_state = + Enum.reduce( + Enum.to_list(1..256), + starting_state, + fn _, acc -> Day06.next_day(acc) end + ) + |> IO.inspect() + |> Enum.reduce(0, fn {_, v}, acc -> v + acc end) +``` + +```output +%{ + 0 => 139170477178, + 1 => 162618979933, + 2 => 169389497028, + 3 => 188231720546, + 4 => 207908029672, + 5 => 217769615201, + 6 => 252681772250, + 7 => 117023886952, + 8 => 138124736869 +} +``` + +```output +1592918715629 +``` diff --git a/racket/aoc2021/day-06/day-06.rkt b/racket/aoc2021/day-06/day-06.rkt new file mode 100644 index 0000000..d8855ba --- /dev/null +++ b/racket/aoc2021/day-06/day-06.rkt @@ -0,0 +1,27 @@ +#lang racket +(require advent-of-code + list-utils + threading + racket/hash) + +(define fish-data + (~> (open-aoc-input (find-session) 2021 6 #:cache (string->path "./cache")) + port->string + string-trim + (string-split ",") + (map string->number _))) + +(define (simulate-fish time-period) + (for/fold ([state (frequencies fish-data)] #:result (~> state hash-values (apply + _))) + ([day (inclusive-range 1 time-period)]) + (define day-older-fish + (for/hash ([(days pop) (in-hash state)]) + (values (sub1 days) pop))) + (define breeding-fish (hash-ref day-older-fish -1 0)) + (hash-union (hash-remove day-older-fish -1) (hash 8 breeding-fish 6 breeding-fish) #:combine +))) + +;; part 1 +(simulate-fish 80) + +;; part 2 +(simulate-fish 256) diff --git a/racket/aoc2021/day-06/input.txt b/racket/aoc2021/day-06/input.txt new file mode 100644 index 0000000..ba3c3cc --- /dev/null +++ b/racket/aoc2021/day-06/input.txt @@ -0,0 +1 @@ +5,4,3,5,1,1,2,1,2,1,3,2,3,4,5,1,2,4,3,2,5,1,4,2,1,1,2,5,4,4,4,1,5,4,5,2,1,2,5,5,4,1,3,1,4,2,4,2,5,1,3,5,3,2,3,1,1,4,5,2,4,3,1,5,5,1,3,1,3,2,2,4,1,3,4,3,3,4,1,3,4,3,4,5,2,1,1,1,4,5,5,1,1,3,2,4,1,2,2,2,4,1,2,5,5,1,4,5,2,4,2,1,5,4,1,3,4,1,2,3,1,5,1,3,4,5,4,1,4,3,3,3,5,5,1,1,5,1,5,5,1,5,2,1,5,1,2,3,5,5,1,3,3,1,5,3,4,3,4,3,2,5,2,1,2,5,1,1,1,1,5,1,1,4,3,3,5,1,1,1,4,4,1,3,3,5,5,4,3,2,1,2,2,3,4,1,5,4,3,1,1,5,1,4,2,3,2,2,3,4,1,3,4,1,4,3,4,3,1,3,3,1,1,4,1,1,1,4,5,3,1,1,2,5,2,5,1,5,3,3,1,3,5,5,1,5,4,3,1,5,1,1,5,5,1,1,2,5,5,5,1,1,3,2,2,3,4,5,5,2,5,4,2,1,5,1,4,4,5,4,4,1,2,1,1,2,3,5,5,1,3,1,4,2,3,3,1,4,1,1 diff --git a/racket/aoc2021/day-07/day-07.rkt b/racket/aoc2021/day-07/day-07.rkt new file mode 100644 index 0000000..89d5009 --- /dev/null +++ b/racket/aoc2021/day-07/day-07.rkt @@ -0,0 +1,28 @@ +#lang racket +(require advent-of-code + threading + math/statistics) + +(define crab-data + (~> (open-aoc-input (find-session) 2021 7 #:cache #t) + port->string + string-trim + (string-split ",") + (map string->number _))) + +(define (gauss-sum n) + (/ (* n (+ n 1)) 2)) +(define (compute-fuel-use f crabs align-to) + (for/sum ([crab (in-list crabs)]) (f (abs (- crab align-to))))) + +;; using the fact that the optimum location is at the median +;; of the crabs' starting location for the linear relationship +;; and at a coordinate within 1 unit of the mean for the quadratic one + +(~>> crab-data (median <) (compute-fuel-use identity crab-data)) + +(~>> crab-data + mean + ((λ (m) (list (floor m) (ceiling m)))) + (map (curry compute-fuel-use gauss-sum crab-data)) + (apply min)) diff --git a/racket/aoc2021/day-08/day-08.rkt b/racket/aoc2021/day-08/day-08.rkt new file mode 100644 index 0000000..6476eae --- /dev/null +++ b/racket/aoc2021/day-08/day-08.rkt @@ -0,0 +1,64 @@ +#lang racket +(require threading + list-utils + "../../jj-aoc.rkt") + +(struct trial-data (signal output) #:transparent) + +(define (string->sets s) + (~> s string-split (map (λ~> string->list list->set) _))) + +(define data + (for/list ([l (in-lines (open-day 8))] #:unless (equal? l "")) + (~> l (string-split _ " | ") (map string->sets _) (apply trial-data _)))) + +;; part 1 +(for*/sum ([trial (in-list data)] [output (in-list (trial-data-output trial))] + #:when (ormap (λ~> (= (set-count output))) '(2 3 4 7))) + 1) + +;; part 2 +(define (matching-pattern len trial) + (define solution-set + (for*/list ([signal (in-list (trial-data-signal trial))] #:when (= (set-count signal) len)) + signal)) + (match solution-set + [(list s) s] + [s (apply set-intersect s)])) + +(define (determine-arrangement t) + (let* ([pattern-1 (matching-pattern 2 t)] + [pattern-4 (matching-pattern 4 t)] + [pattern-7 (matching-pattern 3 t)] + [pattern-8 (matching-pattern 7 t)] + [pattern-shared-235 (matching-pattern 5 t)] + [pattern-3 (set-union pattern-1 pattern-shared-235)] + [pattern-9 (set-union pattern-4 pattern-shared-235)] + [pattern-shared-069 (matching-pattern 6 t)] + [pattern-just-f (set-subtract pattern-shared-069 pattern-shared-235)] + [pattern-just-e + (set-subtract pattern-8 (set-union pattern-4 pattern-shared-235 pattern-shared-069))] + [pattern-2 (set-union (set-subtract pattern-3 pattern-just-f) pattern-just-e)] + [pattern-just-c (set-subtract (set-intersect pattern-4 pattern-7) pattern-just-f)] + [pattern-6 (set-subtract pattern-8 pattern-just-c)] + [pattern-5 (set-subtract pattern-6 pattern-just-e)] + [pattern-0 (set-union (set-subtract pattern-8 pattern-shared-235) pattern-shared-069)]) + (~> (list pattern-0 + pattern-1 + pattern-2 + pattern-3 + pattern-4 + pattern-5 + pattern-6 + pattern-7 + pattern-8 + pattern-9) + enumerate + make-hash))) + +(for/sum ([trial (in-list data)]) + (~>> trial + trial-data-output + (map (λ~>> (hash-ref (determine-arrangement trial)))) + (apply ~a) + string->number)) diff --git a/racket/aoc2021/day-09/day-09.livemd b/racket/aoc2021/day-09/day-09.livemd new file mode 100644 index 0000000..3b984a5 --- /dev/null +++ b/racket/aoc2021/day-09/day-09.livemd @@ -0,0 +1,138 @@ + + + +# Advent of Code 2021, Day 9 + +## Short problem summary + + + +**Part 1.** Find the total "risk level" of all the local minima on a relief map, represented by a +100 $\times$ 100 array of integers from 1 to 9. Only orthogonal neighbors count. +The risk level is the elevation plus one. + +**Part 2.** Find the product of the areas of the three largest basins on the relief map. Basins are regions +bordered by the edges of the map or by points with elevation 9. +Again, only orthogonal neighbors count. + +## Setup + +I'm using [Nx](https://github.com/elixir-nx/nx/tree/main/nx#readme) tensors +since this problem will require a lot of arbitrary indexing. + +```elixir +Mix.install([ + {:nx, "~> 0.1.0-dev", github: "elixir-nx/nx", sparse: "nx", override: true} +]) +``` + +```output +:ok +``` + +Bringing in the data as a new 2D tensor: + +```elixir +floor = + with {:ok, data} <- File.read("input.txt") do + data + |> String.trim() + |> String.split("\n") + |> Enum.flat_map(&String.graphemes/1) + |> Enum.map(&String.to_integer/1) + |> Enum.chunk_every(100) + |> Nx.tensor(names: [:y, :x]) + end +``` + +```output +#Nx.Tensor< + s64[y: 100][x: 100] + [ + [7, 6, 5, 9, 9, 9, 1, 0, 9, 8, 9, 9, 9, 8, 7, 6, 5, 7, 9, 9, 1, 0, 1, 2, 9, 8, 7, 9, 9, 9, 9, 8, 7, 6, 4, 3, 2, 1, 2, 3, 4, 5, 9, 8, 7, 4, 3, 4, 5, 5, ...], + ... + ] +> +``` + +## Part 1 + +For a given coordinate $(x, y)$, we want to examine its orthogonal neighbors, +but only the ones that exist within the map. + +```elixir +defmodule Day09.Part1 do + def neighbors({x, y}) do + [{x + 1, y}, {x - 1, y}, {x, y + 1}, {x, y - 1}] + |> Enum.filter(fn {x, y} -> x >= 0 && x < 100 && y >= 0 && y < 100 end) + end +end +``` + +```output +{:module, Day09.Part1, <<70, 79, 82, 49, 0, 0, 7, ...>>, {:neighbors, 1}} +``` + +Now scan the whole array to check each cell's neighbors, +and return the "risk level" for each local minimum, then accumulate the sum. + +```elixir +risk_level = + for x <- 0..99, + y <- 0..99, + reduce: 0 do + acc -> + Day09.Part1.neighbors({x, y}) + |> Enum.all?(fn {xn, yn} -> floor[x][y] < floor[xn][yn] end) + |> if(do: acc + Nx.to_number(floor[x][y]) + 1, else: acc) + end +``` + +```output +591 +``` + +## Part 2 + +Now we need to recursively walk outwards from each previously-unwalked point +until we can't walk any further. An agent will keep track of the visited points. + +```elixir +defmodule Day09.Part2 do + def walkable?({x, y} = coords, tensor, pid) do + Nx.to_number(tensor[x][y]) < 9 && not Agent.get(pid, fn m -> Map.has_key?(m, coords) end) + end + + def walk_it(coords, tensor, pid) do + if walkable?(coords, tensor, pid) do + Agent.update(pid, fn m -> Map.put(m, coords, true) end) + + for c <- Day09.Part1.neighbors(coords) do + walk_it(c, tensor, pid) + end + |> Enum.reduce(1, fn x, acc -> acc + x end) + else + 0 + end + end +end +``` + +```output +{:module, Day09.Part2, <<70, 79, 82, 49, 0, 0, 11, ...>>, {:walk_it, 3}} +``` + +```elixir +{:ok, tracker} = Agent.start_link(fn -> %{} end) + +for x <- 0..99, y <- 0..99 do + Day09.Part2.walk_it({x, y}, floor, tracker) +end +|> Enum.sort(:desc) +|> Enum.take(3) +|> Enum.reduce(fn x, acc -> acc * x end) +``` + +```output +1113424 +``` diff --git a/racket/aoc2021/day-09/day-09.rkt b/racket/aoc2021/day-09/day-09.rkt new file mode 100644 index 0000000..d550a9e --- /dev/null +++ b/racket/aoc2021/day-09/day-09.rkt @@ -0,0 +1,59 @@ +#lang racket + +(require threading + "../../jj-aoc.rkt") + +(define sea-floor-data + (for/vector ([l (in-lines (open-day 9))] #:unless (equal? l "")) + (~>> l string->list (map (λ~>> ~a string->number)) list->vector))) + +(define max-rows (vector-length sea-floor-data)) +(define max-cols (vector-length (vector-ref sea-floor-data 0))) +(define-values (min-rows min-cols) (values 0 0)) + +(define (vector2d-ref vec coord) + (match-define `(,r ,c) coord) + (~> vec (vector-ref r) (vector-ref c))) + +(define (adjacent coords) + (match-define `(,r ,c) coords) + `((,(add1 r) ,c) (,(sub1 r) ,c) (,r ,(add1 c)) (,r ,(sub1 c)))) + +(define (valid-coordinate coord) + (match-define `(,r ,c) coord) + (and (>= r min-rows) (< r max-rows) (>= c min-cols) (< c max-cols))) + +;; part 1 +(define (lowest-point? vec coord) + (for*/and ([neighbor (in-list (adjacent coord))] #:when (valid-coordinate neighbor)) + (< (vector2d-ref vec coord) (vector2d-ref vec neighbor)))) + +(for*/sum ([r (in-range min-rows max-rows)] [c (in-range min-cols max-cols)] + [coord (in-value `(,r ,c))] + #:when (lowest-point? sea-floor-data coord)) + (add1 (vector2d-ref sea-floor-data coord))) + +;; part 2 +;; all the basins are bordered by the edges or by ridges of elevation 9, +;; so it's not really necessary to start at a low point +(define walked (make-hash)) + +(define (walkable? vec coord) + (and (< (vector2d-ref vec coord) 9) (not (hash-has-key? walked coord)))) + +(define (walk-the-basin vec coord) + (cond + [(walkable? vec coord) + (hash-set! walked coord 'visited) + (add1 (for/sum [(neighbor (in-list (adjacent coord))) #:when (valid-coordinate neighbor)] + (walk-the-basin vec neighbor)))] + [else 0])) + +(define basins + (for*/list ([r (in-range min-rows max-rows)] + [c (in-range min-cols max-cols)] + [coord (in-value `(,r ,c))] + #:when (walkable? sea-floor-data coord)) + (walk-the-basin sea-floor-data coord))) + +(~> basins (sort >) (take 3) (apply * _)) diff --git a/racket/aoc2021/day-09/input.txt b/racket/aoc2021/day-09/input.txt new file mode 100644 index 0000000..322b31f --- /dev/null +++ b/racket/aoc2021/day-09/input.txt @@ -0,0 +1,100 @@ +7659991098999876579910129879999876432123459874345567890126678999876588975767899323456989767899432101 +8998789987898765467891239868899876541012398765123456789234567897643467894656798912399878948678944212 +9867678976789878598954398756789997632343459873234569898765679999856578943547987893989865434567894323 +7654567895896989679767499847994398755456579987656778969876892198767989652129876889876976725678965734 +8767678954345698799898987659943219876577694598787889545987931019879996543298965679965987438789876799 +9898789921239799898989899798794399987688965679898993534598942123989987654987654567894596549899989987 +4969999892398989987876789987689989998789898789979992123989653934598798965976543678943987678999999876 +3459898789497878976545678986567878999895679898768989239879869896789659879865432469432198799998789765 +2498765699976567995434389765434567899934989977655679356965998789896545989865321258921019999987678954 +3987654398765456789321238979325456799329898766434798999876797698987432198754310347894329789876567893 +4696543219876967998910147998214345678998789954323987689999977567898543479885541456789498678988678932 +5987654423987878987521236987601234567891678893219876567898765479987654567976632367899987569899789321 +6798767834698989997432345698524568698932456789398765466989876567898765689987545478959768456789893210 +7899898945679499876545656997434578799843569892987654345678989679999876799798658569349654367878964322 +8977999498789398987896769876545689998767678931999869656789698789999987987698767895498743212567895433 +9656789329898987899929879987676790199898989949877998767896559899878998977569978999987654323458986654 +8797995434987896989434989798989891987919499896765569888921434998767789865452989998998795434569997765 +9979899549876785678945995639698999876329398764354456999990125899545678954321299987899987895678949878 +9865678998765434567899894324567892985498999863212347898989436798434599876210389996789999998789434999 +7654567899896647978989789212459921296987899954393478987678945987324879965341567895679891019998959865 +8543878901987656789765678901268933459876999895989569658567959876412568897432467894599789923987898654 +9212389919898767897654577892999545998765798789878978945456899954323456789576578923989679899876789543 +9543457898769879986543456789889959876543987676567899432345789976436567897697989219878565678965678932 +8754568987654989765432347898767899989432976543456964321012498987545679998989892109867434699764567891 +9867689298543299986541034987656789998743989654677895632134987898696789989878789298754324789543488989 +1978792129654569987732129874543567897654799965789976853239876799989999878765678999865455697601245678 +0989893098765678998653298763212375789795679878994989966398754889879898765464569899976566789212346789 +9898954239987789998784987654301234699989789989873398765459765678968789884323456789987677894323456898 +8777895345698999899895698976214345789878999997762129876589896789345678965434567999898989976564568967 +7656976457899019767987899765423456789569899876543299987678987891234569876545898998769394987875679456 +6546899568992198657898939878534677893456789987654989898789398932347678999856789889843212398986989345 +5435688979879932545679929989665788902569991298779878789899299543458789798767895678932101569997890123 +4323567899767891434589898998776899543457899999898768655978987654569899679878934569643219878998921254 +6764579987658910123456797879887899656568978789987653234567998785678998532989545678954399989999432765 +7875689998767891234567896569998998967989767698798767145678999896789987643498756789765989992987543876 +8976796899878932545678997998769467899899854599659898657789985959897898784569867899876978931098954987 +9697895799989873467889989999652348999799965678943939768999664543956789895679878979989867892129895698 +4598954569899964578999878987643499997689978789432129879998543212345678976989989459899756789298789789 +3499543798798765699998767898984987843579899996583235989987632105468789987899992398789898999987678991 +4985432987679876789999658989876986532498798889874346798798743236589893498989891987678989459986568990 +9876521296567987899876545878989875421987656778965498987669654587679999999876789898547678998765456789 +9876432987678998998765434569998767410996545567896569896556965689789987898865676789435589765432369999 +3987543498799549769886325679987654329877434456789698789439879789899976987764545678923459879321287899 +4599656789989432458997212568999769498764321298999987688956989897999899876743236789212398998932456998 +9798767897678921367989323459998998999875210147899876567897891956789798765432145794301987897893569997 +8999878996569432349876534569987687898654321236789767456789932345679659976545012689419876896789878986 +7899989987458943499998765678976546789985434545678954345679543567789545987983234568998765645678989565 +6789199654347896589989876789987434579876545758789543237789654579895434499874356789329874234567893434 +5679298789756789678976987899874323459987859767899654345678965989965421298765667895499932125689932123 +4568999898969898789765698998765212398498869878998765656789879898965432399876878976987891034567894034 +3456789987898999899894329987654323987349978989999898767896999797896743987987899989896789123678985125 +2346899876767892999989212398765499876567989999899959878934987676989659876798945698765695434679876789 +1256998765458993498979903459876989987698999999789943989949898455678998765759899987654989545689989899 +4349879876569989986567894967989878999789989897678892099898789334569987674545678998743478957897696999 +5478968987678979765456999898998767879899876789546789298788699212989876543234567998932569768998565678 +6568956998789569876567897789999857867998975695437995987657598909898995432123459886521678989895434567 +7678939879892458987678956699896645456987764789567894696543467898767986321019598765432789496789323878 +8789998765901456798789545598775435349876543999698943495432356789656997432198969876547892345689439989 +9895987654312345789899434459654324234987875898789212989321234896549876545997656989856921234579998997 +6954398767433566789998921398743210125698986789894309878540145789432997859876543398767890123467897686 +5695999876544678998787892987654341234589987895999498765431236894320989767987432129898921294878987575 +4989899987698789987656789398765492395678998934998989896549898965999878979876421012969939989999098464 +2976789998789899876543789249876989989989239019887678987656789879878767898985432123457898679889198323 +9895678999894968987654590123989879978990129198764569998987894998765459987899543434568987546778987634 +8654547899923656798985891294598768969893298998765678999598912349821398795698976546699876435569876545 +6543336789012349899876789989987656756789987899976789989499909496543989654567898687987665523456987676 +7652125978923598987987899878996541234567896569899899978987898987859878965678998789876563212345698787 +6543234567894987876798998769876532345978943456799998767896987598998969898799989898765432101234569898 +7685346878999876765689987655989645567899212566778987658965398459987856789899978999876543212385789999 +8876798989998765434569976743498756789956401234568998789876999349876546899988769899987654525678999999 +9987899697989954323798765632359867899543212345679239899989899956997635789879656789999769434789898989 +4599996546567893212987654321235978998764637566989139999998789899986523998967545698999898945699787678 +3499989435457899433498765435348989689895547677891098988997655798765439897645634567899976896789676567 +2989978921345678994569876546757894578987678788992987677893234569898698765430125698999865689896543456 +9879868935458789989778987987868943989998989999789987566989123456989789876321234789997684578965432123 +8965657899599999878989998999979659899879596545678976435778934569879894987432545679876543467896673294 +7654545698989997569999999998989799789965432434789897324567895979964933498545789789987432456998784989 +6543234987978965478999899987899987656974321023498788212379976798763212379676899899997544567899999878 +5432129876568896567898789656789976549875432164589654323568987987654343456987895978999655778956798967 +4321019987456789678987698943495987678987543765678965454569998998765454567898954567898776789345987954 +5432998765345698989996587992976798789798765897789876875678999869876569878979943459979987893212986543 +6549879876234767899985456789897899997659897899897987996789998754997878989767892598965398994323497632 +7698765438123456789876323496789992198943998943956998987899987643298989997656999987890139989434598745 +8899954321014567899865212345678989989894989012345899498989998759109499998747988976789239878965987656 +9998765732125678999954345489789679878789876543456789349678939998912349876434567895678949867896798767 +4349876653346789998769497679898998765699998754678991234569019887893498765325658934589998758659999878 +4239999778659899899898989989967987854897899869789893965678998766799987654312349898699896549237899989 +9398999889767998789987978995459876543786789878898789896799987655678998895401256789798765432126789997 +8987899999879987678976567894398765432645699989997678789929876543767899986212868999899654321034599896 +7856789432989876569875456894298764321237989899986565678910987432347678997343479756998789532123456789 +6545678921098765454986587932129879854349876799867434568924596541234599987654569545689898743454567895 +5436889932129854323697698943234988765698765987654523567895987762345789598968678934578999654765678954 +4321968894298765634598789954445699878987654599763212348997898943579893459899789545678998765878789875 +5872456789349878745679897895768789989877653459894103456789999874568932598789897676789129878989899986 +8763878995467989856789976979879899998765432345989294567899899865679943987699998989899234989299999899 +7654567896568995977891234568999978919876521239878989678956756978989894976568999195978976790199899788 +8765698987878923988910123456789567923987432399765679789432347989998769876456789234567897892988698677 +9878789498999219899321256587995456894898643989813478997643458994987656987567894345679998999876545556 +2989893219879998765432347898932345995798759876524567899856969543499897897698987457989899298765434345 +1099954523467899876545456999545476789899899976435788923987897652101998998789876567897654349854321237 diff --git a/racket/aoc2021/day-10/day-10.rkt b/racket/aoc2021/day-10/day-10.rkt new file mode 100644 index 0000000..ea1b389 --- /dev/null +++ b/racket/aoc2021/day-10/day-10.rkt @@ -0,0 +1,57 @@ +#lang racket + +(require math/statistics + threading + "../../jj-aoc.rkt") + +(define chunks (port->lines (open-day 10 2021))) + +(define (opening-bracket? c) + (member c (string->list "([{<"))) + +(define (matching-brackets? c-left c-right) + (member (string c-left c-right) '("()" "[]" "{}" "<>"))) + +(define (parse-brackets lst [acc '()]) + (cond + [(empty? lst) acc] + [(opening-bracket? (first lst)) (parse-brackets (rest lst) (cons (first lst) acc))] + [(matching-brackets? (first acc) (first lst)) (parse-brackets (rest lst) (rest acc))] + [else (get-score (first lst))])) + +;; part 1 +(define (get-score c) + (match (string c) + [")" 3] + ["]" 57] + ["}" 1197] + [">" 25137])) + +(define (score-invalid-string chunk) + (match (parse-brackets (string->list chunk)) + [(? list?) 0] + [n n])) + +(for/sum ([chunk (in-list chunks)]) (score-invalid-string chunk)) + +;; part 2 +(define (completion-score lst) + (for/fold ([score 0]) ([c (in-list lst)]) + (define val + (match (string c) + ["(" 1] + ["[" 2] + ["{" 3] + ["<" 4])) + (+ (* 5 score) val))) + +(define (score-incomplete-string chunk) + (match (parse-brackets (string->list chunk)) + [(? list? lst) (completion-score lst)] + [n 0])) + +(~>> (for*/list ([chunk (in-list chunks)] + [score (in-value (score-incomplete-string chunk))] + #:when (> score 0)) + score) + (median <)) diff --git a/racket/aoc2021/day-11/day-11.rkt b/racket/aoc2021/day-11/day-11.rkt new file mode 100644 index 0000000..bc22991 --- /dev/null +++ b/racket/aoc2021/day-11/day-11.rkt @@ -0,0 +1,56 @@ +#lang racket +(require "../../jj-aoc.rkt" + threading) + +(define coords + (~>> (for/list ([r (in-range 10)]) + (for/list ([c (in-range 10)]) + (cons r c))) + (apply append))) + +(define octopus-data + (~>> (for/list ([l (in-lines (open-day 11 2021))] #:unless (equal? l "")) + (~>> l string->list (map (λ~>> ~a string->number)))) + (apply append) + (map cons coords) + make-hash)) + +(define total-length (hash-count octopus-data)) +(define row-length (sqrt total-length)) + +(define (adjacent-to coord) + (match-define (cons r c) coord) + (for*/list ([row (in-list '(-1 0 1))] [col (in-list '(-1 0 1))] #:unless (= 0 row col)) + (cons (+ r row) (+ c col)))) + +(define (simulate-octopi-step data) + (define flashed-this-step (mutable-set)) + + (let look-for-more-flashes ([octopi (for/hash ([(k v) data]) + (values k (add1 v)))] + [flashes-so-far 0]) + (define-values (next-octopus-update flashes-this-update) + (for*/fold ([octopi octopi] [flashes 0]) + ([(p x) (in-hash octopi)] #:when (> x 9) #:unless (set-member? flashed-this-step p)) + (set-add! flashed-this-step p) + (define flashed-octopi + (for*/fold ([o (hash-set octopi p 0)]) + ([adj (in-list (adjacent-to p))] + #:when (hash-has-key? o adj) + #:unless (set-member? flashed-this-step adj)) + (hash-update o adj add1))) + (values flashed-octopi (add1 flashes)))) + (if (> flashes-this-update 0) + (look-for-more-flashes next-octopus-update (+ flashes-so-far flashes-this-update)) + (values next-octopus-update flashes-so-far)))) + +;; part 1 +(for/fold ([octopi octopus-data] [total-flashes 0] #:result total-flashes) ([step (in-range 100)]) + (define-values [next-state flashes-from-this-state] (simulate-octopi-step octopi)) + (values next-state (+ total-flashes flashes-from-this-state))) + +;; part 2 +(for/fold ([octopi octopus-data] [synchro-step 0] #:result synchro-step) ([step (in-naturals 1)]) + (define-values [next-state flashes-from-this-state] (simulate-octopi-step octopi)) + #:final (= flashes-from-this-state 100) + (values next-state step)) diff --git a/racket/aoc2021/day-12/day-12.rkt b/racket/aoc2021/day-12/day-12.rkt new file mode 100644 index 0000000..18ed86f --- /dev/null +++ b/racket/aoc2021/day-12/day-12.rkt @@ -0,0 +1,38 @@ +#lang racket +(require "../../jj-aoc.rkt" + threading) + +(define path-pairs + (for/list ([l (in-lines (open-day 12 2021))]) + (match (string-split l "-") + [(list start end) (cons start end)]))) + +(define edges-hash (make-hash)) + +(for ([pair (in-list path-pairs)]) + (match-define (cons start end) pair) + (hash-update! edges-hash start (curry cons end) '()) + (hash-update! edges-hash end (curry cons start) '())) + +;; part 1 +(define (backtracking-disallowed? next prevs) + (and (equal? (string-downcase next) next) (member next prevs))) + +(define (look-for-next-cave [path-list '("start")] #:only-one-visit? [visit-used-up? #t]) + (define current-cave (car path-list)) + (cond + [(equal? current-cave "end") (list path-list)] + [else + (~>> (for/list ([next-path (in-list (hash-ref edges-hash current-cave null))] + #:when (and (not (equal? next-path "start")) + (not (and (backtracking-disallowed? next-path path-list) + visit-used-up?)))) + (look-for-next-cave + (cons next-path path-list) + #:only-one-visit? (or (backtracking-disallowed? next-path path-list) visit-used-up?))) + (apply append))])) + +(~> (look-for-next-cave) length time) + +;; part 2 +(~> (look-for-next-cave #:only-one-visit? #f) length time) diff --git a/racket/aoc2021/day-13/day-13.rkt b/racket/aoc2021/day-13/day-13.rkt new file mode 100644 index 0000000..153eabc --- /dev/null +++ b/racket/aoc2021/day-13/day-13.rkt @@ -0,0 +1,57 @@ +#lang racket +(require "../../jj-aoc.rkt" + threading) + +(define (make-pt x y) + (hash 'x x 'y y)) +(struct fold (dir loc) #:transparent) +(define (make-fold dir loc) + (fold (string->symbol dir) (string->number loc))) + +(define data (port->lines (open-day 13 2021))) +(define-values (points-list folds-list) (splitf-at data (λ~> (equal? "") not))) + +(define pts + (for/set ([pt (in-list points-list)]) + (~> pt (string-split ",") (map string->number _) (apply make-pt _)))) + +(define folds + (for/list ([f (in-list (rest folds-list))]) + (~>> f (regexp-match #px"fold along (.)=(.*)") rest (apply make-fold)))) + +(define (fold-over f pts) + (define dir (fold-dir f)) + (define loc (fold-loc f)) + (for/set ([pt (in-set pts)]) + (cond + [(> (hash-ref pt dir) loc) (hash-update pt dir (λ (l) (- (* 2 loc) l)))] + [else pt]))) + +;; part 1 +(~>> pts (fold-over (first folds)) set-count) + +;; part 2 +(define final-pts + (for/fold ([pt pts]) ([f (in-list folds)]) + (fold-over f pt))) + +(define (max-dim pts dim) + (~>> (for/list ([pt (in-set pts)]) + (hash-ref pt dim)) + (apply max))) + +(for ([y (in-inclusive-range 0 (max-dim final-pts 'y))]) + (~>> (for/list ([x (in-inclusive-range 0 (max-dim final-pts 'x))]) + (if (set-member? final-pts (hash 'x x 'y y)) #\█ #\space)) + (apply string) + println)) + +#| +for this data set, the result looks like +" ██ █ █ ██ ██ ███ ██ ██ █ █" +"█ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █" +"█ █ ████ █ █ █ █ █ █ █ █ █" +"████ █ █ █ ██ █ ███ █ ██ ████ █ █" +"█ █ █ █ █ █ █ █ █ █ █ █ █ █ █" +"█ █ █ █ ███ ██ █ ███ █ █ ██ " +|# diff --git a/racket/aoc2021/day-14/day-14.rkt b/racket/aoc2021/day-14/day-14.rkt new file mode 100644 index 0000000..e445694 --- /dev/null +++ b/racket/aoc2021/day-14/day-14.rkt @@ -0,0 +1,61 @@ +#lang racket +(require "../../jj-aoc.rkt" + threading + memoize + algorithms + list-utils) + +(define data (port->lines (open-day 14 2021))) + +(define (starting-polymer d) + (~> d first string->list (sliding 2 1))) + +(define (first-char d) + (first (first (starting-polymer d)))) + +(define (starting-counts d) + (~> d first frequencies hash->list make-hash)) + +(define (starting-pairs d) + (~>> d (drop _ 2) (map (λ~> (substring 0 2) string->list)))) + +(define (new-pairs d) + (~>> d + (drop _ 2) + (map (λ~> (string-replace " -> " "") + string->list + ((match-lambda + [(list a b c) (list a c b)]) + _) + (sliding 2 1))))) + +(define (transform d) + (~>> (map list (starting-pairs d) (new-pairs d)) (append*) (apply hash))) + +(define transformation (transform data)) + +(define/memo (get-count polymer times) + (match times + [0 + (for/fold ([counts (hash)]) ([pair (in-list polymer)]) + (hash-update counts (second pair) add1 0))] + [_ + (for*/fold ([counts (hash)]) + ([pair (in-list polymer)] + [(c n) (in-hash (get-count (hash-ref transformation pair) (sub1 times)))]) + (hash-update counts c (λ~> (+ n)) 0))])) + +;; part 1 +(define (process-polymer d n) + (~> d + starting-polymer + (get-count _ n) + (hash-update _ (first-char d) add1 0) + hash-values + (sort >) + ((λ (l) (- (first l) (last l)))))) + +(process-polymer data 10) + +;; part 2 +(process-polymer data 40) diff --git a/racket/aoc2021/day-15/day-15-list-nodes.rkt b/racket/aoc2021/day-15/day-15-list-nodes.rkt new file mode 100644 index 0000000..38c558a --- /dev/null +++ b/racket/aoc2021/day-15/day-15-list-nodes.rkt @@ -0,0 +1,67 @@ +#lang racket +(require "../../jj-aoc.rkt" + threading + graph) + +(define data + (for/fold ([cells (hash)]) + ([row (in-lines (open-day 15 2021))] + [x (in-naturals)]) + (for/fold ([cells cells]) + ([n (in-string row)] + [y (in-naturals)]) + (hash-set cells + `(,x ,y) + (~> n string string->number))))) + +(define x-max (~>> data hash-keys (map first) (apply max))) +(define y-max (~>> data hash-keys (map second) (apply max))) + +(define (neighbors pt d) + (match-define (list x y) pt) + (~>> (list (list (add1 x) y) + (list (sub1 x) y) + (list x (add1 y)) + (list x (sub1 y))) + (filter (curry hash-has-key? d)))) + +(define (grid-graph d) + (weighted-graph/directed + (for*/list ([coord (in-list (hash-keys d))] + [neighbor (in-list (neighbors coord d))] + [weight (in-value (hash-ref d neighbor))]) + (list weight coord neighbor)))) + +;; part 1 +(define (find-path-weight d) + (define grid (grid-graph d)) + (let-values ([(node-distances _) (dijkstra grid '(0 0))]) + (define xm (~>> d hash-keys (map first) (apply max))) + (define ym (~>> d hash-keys (map second) (apply max))) + (hash-ref node-distances (list xm ym)))) + +(~> data + find-path-weight + time) + +;; part 2 +(define nine-cycle + (in-cycle (inclusive-range 1 9))) + +(define (expand-data data) + (for/fold ([cells (hash)]) + ([coord (in-list (hash-keys data))]) + (match-define (list x y) coord) + (for*/fold ([cells cells]) + ([n (in-range 5)] + [m (in-range 5)] + [val (in-value (hash-ref data coord))]) + (hash-set cells + (list (+ x (* n (add1 x-max))) + (+ y (* m (add1 y-max)))) + (sequence-ref nine-cycle (+ val n m -1)))))) + +(~> data + expand-data + find-path-weight + time) \ No newline at end of file diff --git a/racket/aoc2021/day-15/day-15.livemd b/racket/aoc2021/day-15/day-15.livemd new file mode 100644 index 0000000..2495c32 --- /dev/null +++ b/racket/aoc2021/day-15/day-15.livemd @@ -0,0 +1,287 @@ + + + +# Advent of Code 2021, Day 15 + +## Short summary + +**Parts 1 and 2.** Find the least "risky" path through a grid of nodes, +where each node has a "risk" cost to enter. Part 1's grid is the given data, +while Part 2's is the data after undergoing a transformation. +Only orthogonal travel is possible. + +## Setup + +Using the `libgraph` library to build the graph +and use its implementation of the Dijkstra algorithm: + +```elixir +Mix.install([ + {:libgraph, github: "bitwalker/libgraph"} +]) +``` + +```output +* Getting libgraph (https://github.com/bitwalker/libgraph.git) +remote: Enumerating objects: 783, done. +remote: Counting objects: 100% (64/64), done. +remote: Compressing objects: 100% (52/52), done. +remote: Total 783 (delta 27), reused 24 (delta 10), pack-reused 719 +origin/HEAD set to main +==> libgraph +Compiling 14 files (.ex) +Generated libgraph app +``` + +```output +:ok +``` + +```elixir +floor = + with {:ok, data} <- File.read("./2021/day-15/input") do + data + |> String.split() + |> Enum.map(fn xs -> + String.graphemes(xs) + |> Enum.map(&String.to_integer/1) + end) + end +``` + +```output +[ + [4, 5, 5, 2, 2, 8, 5, 9, 8, 9, 4, 4, 1, 1, 2, 4, 7, 1, 9, 7, 9, 8, 4, 6, 5, 8, 2, 5, 7, 7, 3, 3, + 1, 8, 2, 5, 2, 2, 6, 9, 4, 2, 2, 6, 2, 5, 7, 3, 1, ...], + [8, 3, 1, 1, 8, 2, 6, 9, 1, 7, 6, 7, 7, 2, 9, 5, 5, 6, 1, 3, 9, 5, 7, 1, 6, 2, 4, 5, 4, 2, 6, 1, + 8, 4, 2, 1, 2, 4, 7, 1, 3, 1, 1, 2, 1, 4, 8, 5, ...], + [3, 1, 3, 4, 1, 5, 5, 5, 2, 8, 2, 3, 1, 2, 9, 6, 3, 1, 3, 2, 9, 2, 9, 5, 1, 6, 8, 9, 3, 7, 1, 6, + 7, 1, 6, 1, 1, 3, 5, 5, 1, 9, 9, 4, 2, 9, 3, ...], + [7, 1, 3, 4, 1, 5, 9, 6, 8, 1, 5, 3, 3, 3, 3, 1, 1, 7, 2, 9, 9, 1, 9, 3, 2, 8, 1, 9, 1, 2, 8, 7, + 1, 8, 1, 3, 1, 1, 2, 1, 4, 2, 8, 7, 1, 5, ...], + [3, 2, 9, 1, 6, 2, 2, 1, 2, 1, 8, 9, 3, 8, 2, 1, 2, 5, 8, 2, 1, 5, 1, 1, 1, 5, 3, 1, 6, 1, 4, 2, + 3, 3, 9, 9, 9, 6, 6, 1, 6, 4, 9, 2, 7, ...], + [9, 1, 1, 6, 1, 9, 7, 3, 1, 9, 9, 9, 2, 2, 2, 3, 7, 8, 4, 9, 7, 3, 7, 4, 9, 1, 5, 9, 9, 1, 2, 9, + 1, 8, 9, 4, 8, 5, 1, 4, 8, 5, 5, 2, ...], + [4, 9, 6, 8, 4, 3, 9, 1, 1, 4, 1, 3, 5, 9, 9, 6, 9, 2, 3, 9, 3, 9, 6, 2, 1, 5, 2, 1, 2, 2, 7, 4, + 3, 8, 6, 1, 6, 4, 3, 2, 2, 6, 2, ...], + [6, 2, 3, 9, 2, 1, 9, 7, 8, 5, 2, 3, 4, 6, 7, 5, 3, 9, 1, 2, 8, 1, 6, 4, 1, 1, 1, 4, 1, 4, 8, 2, + 2, 5, 9, 4, 2, 6, 2, 2, 1, 9, ...], + [1, 9, 3, 9, 2, 7, 3, 1, 5, 2, 1, 1, 4, 1, 6, 5, 8, 1, 5, 6, 2, 1, 8, 9, 8, 1, 3, 1, 7, 4, 1, 6, + 2, 5, 1, 4, 2, 2, 7, 3, 7, ...], + [3, 8, 1, 6, 2, 9, 5, 1, 2, 1, 2, 1, 1, 7, 1, 3, 9, 1, 9, 9, 1, 8, 1, 2, 9, 1, 4, 1, 5, 2, 1, 7, + 4, 8, 3, 1, 5, 5, 7, 9, ...], + [2, 8, 5, 2, 1, 7, 4, 3, 9, 2, 1, 7, 5, 2, 1, 7, 8, 9, 1, 2, 9, 2, 7, 6, 9, 2, 9, 7, 3, 2, 9, 7, + 3, 4, 1, 4, 1, 5, 2, ...], + [8, 1, 8, 3, 4, 1, 1, 9, 8, 1, 1, 3, 6, 7, 1, 8, 4, 2, 8, 8, 2, 3, 1, 2, 7, 6, 8, 1, 4, 1, 1, 2, + 4, 6, 5, 2, 8, 2, ...], + [3, 3, 3, 8, 4, 1, 8, 9, 5, 8, 8, 1, 8, 2, 2, 9, 3, 3, 1, 3, 9, 9, 5, 1, 3, 6, 9, 1, 9, 2, 1, 9, + 2, 1, 8, 6, 9, ...], + [6, 1, 1, 8, 5, 8, 2, 7, 6, 5, 3, 7, 5, 5, 4, 1, 1, 4, 4, 4, 9, 1, 5, 1, 9, 8, 7, 1, 1, 2, 3, 1, + 2, 1, 1, 7, ...], + [1, 4, 6, 9, 2, 5, 7, 4, 1, 8, 3, 1, 3, 2, 1, 8, 9, 5, 1, 2, 1, 1, 5, 1, 1, 5, 9, 1, 2, 2, 4, 2, + 8, 1, 8, ...], + [1, 4, 2, 2, 1, 1, 8, 4, 1, 9, 3, 1, 9, 5, 1, 5, 4, 9, 6, 1, 8, 9, 3, 9, 1, 1, 5, 8, 5, 1, 6, 4, + 6, 2, ...], + [8, 1, 6, 2, 5, 4, 6, 1, 3, 9, 9, 5, 2, 1, 3, 6, 1, 8, 9, 9, 1, 1, 1, 7, 7, 5, 3, 3, 3, 5, 7, 8, + 2, ...], + [1, 4, 8, 8, 2, 3, 9, 6, 1, 1, 4, 6, 1, 4, 9, 1, 9, 7, 3, 8, 6, 5, 1, 8, 5, 2, 4, 2, 9, 5, 9, 5, + ...], + [2, 2, 1, 8, 9, 1, 7, 6, 6, 1, 8, 6, 8, 1, 3, 3, 1, 4, 1, 2, 4, 3, 8, 1, 9, 9, 6, 2, 4, 1, 3, ...], + [5, 7, 3, 4, 3, 2, 6, 7, 3, 2, 8, 8, 4, 7, 9, 2, 4, 2, 8, 1, 9, 1, 2, 4, 1, 1, 2, 1, 1, 9, ...], + [1, 5, 1, 1, 9, 3, 5, 1, 1, 1, 8, 6, 1, 1, 2, 1, 9, 3, 5, 7, 4, 1, 7, 6, 8, 3, 3, 8, 9, ...], + [9, 6, 9, 1, 5, 6, 2, 1, 8, 3, 5, 1, 3, 3, 8, 8, 2, 2, 1, 7, 2, 6, 4, 9, 3, 8, 2, 1, ...], + [2, 7, 2, 9, 2, 3, 7, 5, 3, 2, 9, 5, 9, 4, 4, 4, 7, 3, 3, 2, 2, 5, 4, 3, 5, 9, 9, ...], + [7, 3, 1, 2, 5, 5, 9, 1, 8, 1, 3, 2, 3, 7, 4, 3, 9, 3, 8, 2, 2, 1, 2, 1, 2, 2, ...], + [9, 4, 1, 5, 7, 4, 7, 7, 3, 8, 3, 4, 6, 1, 4, 1, 6, 9, 1, 4, 1, 8, 8, 2, 5, ...], + [1, 7, 1, 6, 2, 6, 3, 1, 2, 1, 1, 1, 5, 6, 3, 1, 1, 4, 1, 7, 8, 4, 2, 5, ...], + [2, 1, 1, 3, 4, 1, 4, 9, 1, 1, 7, 3, 1, 1, 6, 4, 9, 6, 2, 3, 4, 2, 2, ...], + [5, 6, 1, 1, 9, 1, 2, 3, 3, 4, 9, 2, 7, 2, 2, 1, 2, 5, 2, 7, 1, 1, ...], + [1, 2, 3, 2, 3, 1, 3, 2, 7, 1, 1, 1, 3, 5, 5, 4, 1, 8, 9, 9, 5, ...], + [2, 9, 2, 8, 1, 2, 3, 1, 2, 1, 1, 9, 1, 2, 6, 7, 7, 2, 1, 3, ...], + [3, 7, 3, 8, 1, 5, 2, 5, 4, 4, 6, 2, 5, 5, 1, 6, 9, 1, 6, ...], + [4, 9, 7, 8, 9, 3, 2, 2, 3, 7, 3, 1, 2, 6, 1, 5, 4, 5, ...], + [3, 9, 1, 2, 8, 9, 9, 1, 8, 2, 7, 9, 8, 9, 1, 6, 6, ...], + [1, 1, 9, 2, 9, 2, 7, 4, 2, 1, 3, 7, 2, 5, 8, 4, ...], + [3, 9, 1, 1, 1, 8, 5, 2, 1, 5, 5, 8, 2, 4, 3, ...], + [2, 9, 5, 3, 2, 1, 6, 7, 2, 2, 8, 1, 5, 2, ...], + [1, 1, 9, 8, 3, 8, 2, 7, 6, 7, 2, 1, 8, ...], + [2, 7, 2, 1, 8, 8, 4, 4, 1, 2, 9, 4, ...], + [3, 1, 1, 5, 1, 1, 8, 2, 2, 6, 2, ...], + [1, 4, 6, 6, 1, 3, 8, 1, 5, 2, ...], + [9, 1, 2, 2, 1, 3, 4, 4, 5, ...], + [6, 6, 1, 9, 4, 1, 3, 2, ...], + [1, 5, 9, 1, 8, 4, 5, ...], + [9, 1, 4, 5, 6, 7, ...], + [1, 7, 5, 6, 3, ...], + [7, 1, 1, 5, ...], + [1, 5, 9, ...], + [1, 5, ...], + [2, ...], + [...], + ... +] +``` + +Give each node a label based on its coordinates: + +```elixir +floor_nodes = + for {row, i} <- Enum.with_index(floor), + {col, j} <- Enum.with_index(row), + into: %{} do + {{i, j}, col} + end +``` + +```output +%{ + {76, 13} => 1, + {37, 47} => 2, + {65, 63} => 5, + {38, 2} => 1, + {1, 26} => 4, + {83, 76} => 2, + {32, 15} => 6, + {89, 14} => 1, + {35, 30} => 7, + {37, 53} => 7, + {4, 5} => 2, + {8, 50} => 7, + {78, 98} => 7, + {95, 56} => 7, + {74, 12} => 9, + {11, 39} => 2, + {65, 43} => 4, + {22, 38} => 1, + {14, 86} => 4, + {20, 41} => 1, + {29, 25} => 1, + {86, 10} => 1, + {83, 36} => 3, + {29, 26} => 3, + {47, 27} => 9, + {4, 81} => 3, + {31, 42} => 1, + {9, 34} => 3, + {90, 0} => 3, + {67, 98} => 1, + {13, 85} => 1, + {63, 81} => 4, + {82, 60} => 4, + {47, 38} => 1, + {15, 92} => 1, + {58, 58} => 1, + {20, 3} => 1, + {61, 95} => 7, + {23, 67} => 4, + {78, 75} => 1, + {79, 17} => 2, + {75, 0} => 7, + {16, 73} => 2, + {76, 2} => 8, + {58, 84} => 1, + {58, 33} => 7, + {47, 44} => 2, + {54, 31} => 6, + {13, ...} => 1, + {...} => 9, + ... +} +``` + +We can travel in the four cardinal directions from each node, so we need +a function to identify a node's neighbors: + +```elixir +neighbors = fn {i, j}, nodes -> + [{i + 1, j}, {i - 1, j}, {i, j + 1}, {i, j - 1}] + |> Enum.filter(&Map.has_key?(nodes, &1)) +end + +[neighbors.({0, 0}, floor_nodes), neighbors.({50, 50}, floor_nodes)] +``` + +```output +[[{1, 0}, {0, 1}], [{51, 50}, {49, 50}, {50, 51}, {50, 49}]] +``` + +## Part 1 + +Now we fold all the edges into a `Graph`: + +```elixir +make_graph = fn nodes -> + for {coord, _} <- nodes, + neighbor <- neighbors.(coord, nodes), + risk = Map.get(nodes, neighbor), + reduce: Graph.new(vertex_identifier: fn v -> v end) do + acc -> Graph.add_edge(acc, coord, neighbor, weight: risk) + end +end + +floor_graph = make_graph.(floor_nodes) +``` + +```output +#Graph +``` + +Now we just need to use Dijkstra's algorithm to find the best path from the upper left to the lower right, +and use the map of each node's risk value to sum up the total risk for the path. + +```elixir +get_lowest_risk_path = fn nodes -> + with {{min_c, _}, {max_c, _}} <- Enum.min_max_by(nodes, fn {{i, j}, _} -> i + j end) do + nodes + |> then(make_graph) + |> Graph.dijkstra(min_c, max_c) + |> tl() + |> Enum.reduce(0, fn coord, sum -> sum + Map.get(nodes, coord) end) + end +end + +get_lowest_risk_path.(floor_nodes) +``` + +```output +403 +``` + +## Part 2 + +The process for Part 2 will be similar; the only difference +is that the graph is five times bigger after the transform. + +If the transformed risk is greater than 9, it rolls over to 1, so a +`Stream.cycle` can be used to easily get the rolled-over values. + +```elixir +expanded_floor_nodes = + with {{max_i, max_j}, _} <- Enum.max_by(floor_nodes, fn {{i, j}, _} -> i + j end), + nine_cycle = Stream.cycle(1..9) do + for {{i, j}, risk} <- floor_nodes, + x <- 0..4, + y <- 0..4, + into: %{} do + {{i + x * (max_i + 1), j + y * (max_j + 1)}, Enum.at(nine_cycle, risk - 1 + x + y)} + end + end + +Enum.count(expanded_floor_nodes) +``` + +```output +250000 +``` + +We repeat the same steps as before: building the graph, +using Dijkstra's algorithm and summing up the risks along the best path. + +```elixir +get_lowest_risk_path.(expanded_floor_nodes) +``` + +```output +2840 +``` diff --git a/racket/aoc2021/day-15/day-15.rkt b/racket/aoc2021/day-15/day-15.rkt new file mode 100644 index 0000000..6ab67b1 --- /dev/null +++ b/racket/aoc2021/day-15/day-15.rkt @@ -0,0 +1,50 @@ +#lang racket +(require advent-of-code + threading + graph) + +(struct Point (x y) #:transparent) + +(define data + (for/fold ([cells (hash)]) + ([row (in-lines (open-aoc-input (find-session) 2021 15 #:cache #true))] [x (in-naturals)]) + (for/fold ([cells cells]) ([n (in-string row)] [y (in-naturals)]) + (hash-set cells (Point x y) (~> n string string->number))))) + +(define x-max (~>> data hash-keys (map Point-x) (apply max))) +(define y-max (~>> data hash-keys (map Point-y) (apply max))) + +(define (neighbors pt d) + (match-define (Point x y) pt) + (~>> (list (Point (add1 x) y) (Point (sub1 x) y) (Point x (add1 y)) (Point x (sub1 y))) + (filter (curry hash-has-key? d)))) + +(define (grid-graph d) + (weighted-graph/directed (for*/list ([coord (in-list (hash-keys d))] + [neighbor (in-list (neighbors coord d))] + [weight (in-value (hash-ref d neighbor))]) + (list weight coord neighbor)))) + +;; part 1 +(define (find-path-weight d) + (define grid (grid-graph d)) + (let-values ([(node-distances _) (dijkstra grid (Point 0 0))]) + (define xm (~>> d hash-keys (map Point-x) (apply max))) + (define ym (~>> d hash-keys (map Point-y) (apply max))) + (hash-ref node-distances (Point xm ym)))) + +(~> data find-path-weight time) + +;; part 2 +(define nine-cycle (in-cycle (inclusive-range 1 9))) + +(define (expand-data data) + (for/fold ([cells (hash)]) ([coord (in-list (hash-keys data))]) + (match-define (Point x y) coord) + (for*/fold ([cells cells]) + ([n (in-range 5)] [m (in-range 5)] [val (in-value (hash-ref data coord))]) + (hash-set cells + (Point (+ x (* n (add1 x-max))) (+ y (* m (add1 y-max)))) + (sequence-ref nine-cycle (+ val n m -1)))))) + +(~> data expand-data find-path-weight time) diff --git a/racket/aoc2021/day-16/day-16.rkt b/racket/aoc2021/day-16/day-16.rkt new file mode 100644 index 0000000..86083ef --- /dev/null +++ b/racket/aoc2021/day-16/day-16.rkt @@ -0,0 +1,97 @@ +#lang racket +(require "../../jj-aoc.rkt" + bitsyntax + threading) + +(struct packet (version type type-id contents len) #:transparent) + +(define (BITS->bitstring str) + (integer->bit-string (string->number str 16) (* 4 (string-length str)) #true)) + +(define data (~> (open-day 16 2021) port->string string-trim BITS->bitstring)) + +(define (get-literal-contents bitstr) + (for/fold ([assembled (bit-string)] + [remaining bitstr] + [total-length 6] + [complete? #f] + #:result (values (bit-string->integer assembled #t #f) remaining total-length)) + ([_ (in-naturals)] #:break complete?) + (bit-string-case remaining + ([(= 1 :: bits 1) (number :: bits 4) (remaining :: binary)] + (values (bit-string-append assembled (integer->bit-string number 4 #t)) + remaining + (+ total-length 5) + #f)) + ([(= 0 :: bits 1) (number :: bits 4) (remaining :: binary)] + (values (bit-string-append assembled (integer->bit-string number 4 #t)) + remaining + (+ total-length 5) + #t))))) + +(define (get-type-0-contents cnt bitstr [acc '()] [len 0]) + (cond + [(<= cnt 0) (values (reverse acc) bitstr len)] + [else + (define-values (packet remaining) (identify-next-packet bitstr)) + (get-type-0-contents (- cnt (packet-len packet)) + remaining + (cons packet acc) + (+ len (packet-len packet)))])) + +(define (get-type-1-contents cnt bitstr [acc '()] [len 0]) + (cond + [(= cnt 0) (values (reverse acc) bitstr len)] + [else + (define-values (packet remaining) (identify-next-packet bitstr)) + (get-type-1-contents (sub1 cnt) remaining (cons packet acc) (+ len (packet-len packet)))])) + +(define (identify-next-packet bitstr) + (bit-string-case + bitstr + ([(packet-version :: bits 3) (= 4 :: bits 3) (remaining :: binary)] + (define-values (n now-remaining len) (get-literal-contents remaining)) + (values (packet packet-version 'literal 4 n len) now-remaining)) + ([(packet-version :: bits 3) + (type-id :: bits 3) + (= 0 :: bits 1) + (subpacket-length :: bits 15) + (remaining :: binary)] + (define-values (contents now-remaining sublength) + (get-type-0-contents subpacket-length remaining)) + (values (packet packet-version 'operator type-id contents (+ 22 sublength)) now-remaining)) + ([(packet-version :: bits 3) + (type-id :: bits 3) + (= 1 :: bits 1) + (subpacket-count :: bits 11) + (remaining :: binary)] + (define-values (contents now-remaining sublength) (get-type-1-contents subpacket-count remaining)) + (values (packet packet-version 'operator type-id contents (+ 22 sublength)) now-remaining)))) + +(match-define-values (outer-packet n) (identify-next-packet data)) + +;; part 1 +(define (packet-sum-version p) + (match p + [(packet v 'literal _type-id _contents _len) v] + [(packet v 'operator _type-id ps _len) (foldl (λ (p acc) (+ acc (packet-sum-version p))) v ps)])) + +(packet-sum-version outer-packet) + +;; part 2 +(define packet-f + (match-lambda + [0 +] + [1 *] + [2 min] + [3 max] + [5 (λ (a b) (if (> a b) 1 0))] + [6 (λ (a b) (if (< a b) 1 0))] + [7 (λ (a b) (if (= a b) 1 0))])) + +(define packet-eval + (match-lambda + [(packet _v 'literal _type-id n _len) n] + [(packet _v 'operator type-id ps _len) (~>> ps (map packet-eval) (apply (packet-f type-id)))])) + +(packet-eval outer-packet) diff --git a/racket/aoc2021/day-17/day-17.rkt b/racket/aoc2021/day-17/day-17.rkt new file mode 100644 index 0000000..7de44a0 --- /dev/null +++ b/racket/aoc2021/day-17/day-17.rkt @@ -0,0 +1,43 @@ +#lang racket +(require "../../jj-aoc.rkt" + threading) + +(define-values (x-min x-max y-min y-max) + (~> (open-day 17 2021) + port->string + (regexp-match #px"target area: x=(.*)\\.\\.(.*), y=(.*)\\.\\.(.*)\n" _) + rest + (map string->number _) + (apply values _))) + +(define (hit? x y) + (and (x . >= . x-min) (x . <= . x-max) (y . >= . y-min) (y . <= . y-max))) + +(define (miss? x y) + (or (y . < . y-min) (x . > . x-max))) + +(define (drag dx i) + (max (- dx i) 0)) +(define (gravity dy i) + (- dy i)) + +(define (find-trajectory-apex dx dy) + (for/fold ([x 0] [y 0] [y-apex 0] [result #f] #:result (list y-apex result)) + ([i (in-naturals)] #:break result) + (cond + [(hit? x y) (values dx dy y-apex 'hit)] + [(miss? x y) (values x y 'miss 'miss)] + [else (values (+ x (drag dx i)) (+ y (gravity dy i)) (if (y . > . y-apex) y y-apex) #f)]))) + +(define on-target + (for*/list ([dx (in-inclusive-range 1 x-max)] + [dy (in-inclusive-range y-min (abs (* 2 y-max)))] + [velocity (in-value (find-trajectory-apex dx dy))] + #:when (equal? (second velocity) 'hit)) + (list dx dy (first velocity)))) + +;; part 1 +(~>> on-target (argmax third) third) + +;; part 2 +(length on-target) diff --git a/racket/aoc2021/day-18/day-18.rkt b/racket/aoc2021/day-18/day-18.rkt new file mode 100644 index 0000000..45016b1 --- /dev/null +++ b/racket/aoc2021/day-18/day-18.rkt @@ -0,0 +1,5 @@ +#lang racket +(require "../../jj-aoc.rkt" + threading) + +;; tbd \ No newline at end of file diff --git a/racket/aoc2021/day-19/day-19.rkt b/racket/aoc2021/day-19/day-19.rkt new file mode 100644 index 0000000..4c6334d --- /dev/null +++ b/racket/aoc2021/day-19/day-19.rkt @@ -0,0 +1,48 @@ +#lang racket +(require "../../jj-aoc.rkt" + threading + racket/struct) + +(struct coord (x y z) #:transparent) + +(define (coord-broadcast f c1 c2) + (match-define (coord x1 y1 z1) c1) + (match-define (coord x2 y2 z2) c2) + (coord (f x1 x2) (f y1 y2) (f z1 z2))) + +(define (coord-reduce f c1 c2) + (foldl (λ (i1 i2 acc) (+ acc (f i1 i2))) 0 (struct->list c1) (struct->list c2))) + +(define coord-delta (curry coord-broadcast -)) +(define coord-sum (curry coord-broadcast +)) +(define coord-manhattan (curry coord-reduce (coord 0 0 0))) + +(define (create-scan-data d) + (for/list ([l (in-list d)]) + (for/list ([pt (in-list (~> (string-split l "\n") rest))]) + (~>> pt + (regexp-match #px"(-?\\d+),(-?\\d+),(-?\\d+)") + rest + (map string->number) + (apply coord))))) + +(define scanner-data (create-scan-data (~>> (open-day 19 2021) port->string (string-split _ "\n\n")))) + +(define (generate-rotations scanner) + (apply + map + list + (for*/list ([pt (in-list scanner)]) + (match-define (coord x y z) pt) + (define orientations (list (list x y z) (list x z (- y)) (list x (- y) (- z)) (list x (- z) y))) + (append* (for/list ([o (in-list orientations)]) + (match-define (list x* y* z*) o) + (list (list x y z) + (list (- x) z y) + (list z (- y) x) + (list y x (- z)) + (list (- y) (- z) x))))))) + +(define (find-overlaps scan1 scan2) + (for/list ([rotation (in-permutations scan2)]) + (map coord-sum scan1 rotation))) diff --git a/racket/aoc2021/day-19/test-scanners b/racket/aoc2021/day-19/test-scanners new file mode 100644 index 0000000..b596cc4 --- /dev/null +++ b/racket/aoc2021/day-19/test-scanners @@ -0,0 +1,136 @@ +--- scanner 0 --- +404,-588,-901 +528,-643,409 +-838,591,734 +390,-675,-793 +-537,-823,-458 +-485,-357,347 +-345,-311,381 +-661,-816,-575 +-876,649,763 +-618,-824,-621 +553,345,-567 +474,580,667 +-447,-329,318 +-584,868,-557 +544,-627,-890 +564,392,-477 +455,729,728 +-892,524,684 +-689,845,-530 +423,-701,434 +7,-33,-71 +630,319,-379 +443,580,662 +-789,900,-551 +459,-707,401 + +--- scanner 1 --- +686,422,578 +605,423,415 +515,917,-361 +-336,658,858 +95,138,22 +-476,619,847 +-340,-569,-846 +567,-361,727 +-460,603,-452 +669,-402,600 +729,430,532 +-500,-761,534 +-322,571,750 +-466,-666,-811 +-429,-592,574 +-355,545,-477 +703,-491,-529 +-328,-685,520 +413,935,-424 +-391,539,-444 +586,-435,557 +-364,-763,-893 +807,-499,-711 +755,-354,-619 +553,889,-390 + +--- scanner 2 --- +649,640,665 +682,-795,504 +-784,533,-524 +-644,584,-595 +-588,-843,648 +-30,6,44 +-674,560,763 +500,723,-460 +609,671,-379 +-555,-800,653 +-675,-892,-343 +697,-426,-610 +578,704,681 +493,664,-388 +-671,-858,530 +-667,343,800 +571,-461,-707 +-138,-166,112 +-889,563,-600 +646,-828,498 +640,759,510 +-630,509,768 +-681,-892,-333 +673,-379,-804 +-742,-814,-386 +577,-820,562 + +--- scanner 3 --- +-589,542,597 +605,-692,669 +-500,565,-823 +-660,373,557 +-458,-679,-417 +-488,449,543 +-626,468,-788 +338,-750,-386 +528,-832,-391 +562,-778,733 +-938,-730,414 +543,643,-506 +-524,371,-870 +407,773,750 +-104,29,83 +378,-903,-323 +-778,-728,485 +426,699,580 +-438,-605,-362 +-469,-447,-387 +509,732,623 +647,635,-688 +-868,-804,481 +614,-800,639 +595,780,-596 + +--- scanner 4 --- +727,592,562 +-293,-554,779 +441,611,-461 +-714,465,-776 +-743,427,-804 +-660,-479,-426 +832,-632,460 +927,-485,-438 +408,393,-506 +466,436,-512 +110,16,151 +-258,-428,682 +-393,719,612 +-211,-452,876 +808,-476,-593 +-575,615,604 +-485,667,467 +-680,325,-822 +-627,-443,-432 +872,-547,-609 +833,512,582 +807,604,487 +839,-516,451 +891,-625,532 +-652,-548,-490 +30,-46,-14 \ No newline at end of file diff --git a/racket/aoc2021/day-20/day-20.rkt b/racket/aoc2021/day-20/day-20.rkt new file mode 100644 index 0000000..b7ed092 --- /dev/null +++ b/racket/aoc2021/day-20/day-20.rkt @@ -0,0 +1,59 @@ +#lang racket +(require "../../jj-aoc.rkt" + threading) + +(struct pixel (i j) #:transparent) + +(define-values (raw-enhancement raw-image) + (~> (open-day 20 2021) port->string (string-split "\n\n") (apply values _))) + +(define (char->pixel c) + (if (char=? #\# c) 'light 'dark)) +(define (pixel->char c) + (if (equal? 'light c) #\# #\.)) + +(define enhancement-algorithm + (for/hash ([(c i) (in-indexed (~> raw-enhancement (string-replace "\n" "")))]) + (values i (char->pixel c)))) + +(define image-hash + (for*/hash ([(row i) (in-indexed (string-split raw-image "\n"))] [(c j) (in-indexed row)]) + (values (pixel i j) (char->pixel c)))) + +(define (window->new-pixel p ps bg) + (match-define (pixel i j) p) + (~> (for*/list ([di '(-1 0 1)] [dj '(-1 0 1)]) + (if (equal? 'dark (hash-ref ps (pixel (+ i di) (+ j dj)) bg)) #\0 #\1)) + (apply string _) + (string->number _ 2) + (hash-ref enhancement-algorithm _))) + +(define (pad-hash ps bg) + (define coords (hash-keys ps)) + (define i-min (~>> coords (map pixel-i) (apply min))) + (define i-max (~>> coords (map pixel-i) (apply max))) + (define j-min (~>> coords (map pixel-j) (apply min))) + (define j-max (~>> coords (map pixel-j) (apply max))) + (for*/hash ([i (in-inclusive-range (- i-min 2) (+ i-max 2))] + [j (in-inclusive-range (- j-min 2) (+ j-max 2))]) + (values (pixel i j) (hash-ref ps (pixel i j) bg)))) + +(define (enhance-image ps bg) + (for/hash ([(p _) (in-hash (pad-hash ps bg))]) + (values p (window->new-pixel p ps bg)))) + +;; part 1 +;; looking at the enhancement algorithm, since a window of 0 -> light and 512 -> dark, +;; the infinite background flips colors every other enhancement step +;; instead of trying to account for this algorithmically I just hardcoded it in +(~> image-hash + (enhance-image 'dark) + (enhance-image 'light) + hash-values + (count (curry equal? 'light) _)) + +;; part 2 +(~> (for/fold ([img image-hash]) ([_ (in-range 25)]) + (~> img (enhance-image 'dark) (enhance-image 'light))) + hash-values + (count (curry equal? 'light) _)) diff --git a/racket/aoc2021/day-21/day-21.rkt b/racket/aoc2021/day-21/day-21.rkt new file mode 100644 index 0000000..9ca9b1b --- /dev/null +++ b/racket/aoc2021/day-21/day-21.rkt @@ -0,0 +1,59 @@ +#lang racket +(require threading + memoize) + +;; not going to bother importing the data since it's just two lines of text +(define player-1-start 4) +(define player-2-start 6) + +(define track (sequence->stream (in-cycle (inclusive-range 1 10)))) +(define current-turn (in-cycle (list 'player-1 'player-2))) +(define die-rolls (sequence->stream (in-cycle (inclusive-range 1 100)))) + +;; part 1 +(~> (for/fold ([player-1-score 0] + [player-1-track (stream-tail track (sub1 player-1-start))] + [player-2-score 0] + [player-2-track (stream-tail track (sub1 player-2-start))] + [dice die-rolls] + [last-turn 0] + #:result (list (min player-1-score player-2-score) (* 3 last-turn))) + ([turn-count (in-naturals 1)] + [turn-for current-turn] + #:break (or (player-1-score . >= . 1000) (player-2-score . >= . 1000))) + (define rolls (apply + (stream->list (stream-take dice 3)))) + (match turn-for + ['player-1 + (define next-track (stream-tail player-1-track rolls)) + (values (+ player-1-score (stream-first next-track)) + next-track + player-2-score + player-2-track + (stream-tail dice 3) + turn-count)] + ['player-2 + (define next-track (stream-tail player-2-track rolls)) + (values player-1-score + player-1-track + (+ player-2-score (stream-first next-track)) + next-track + (stream-tail dice 3) + turn-count)])) + (apply * _)) + +;; part 2 +(define d3 (list 1 2 3)) +(define roll-space (~>> (cartesian-product d3 d3 d3) (map (λ~>> (apply +))))) + +(define/memo + (next-turns p1-score p2-score p1-start p2-start) + (cond + [(p1-score . >= . 21) '(1 0)] + [(p2-score . >= . 21) '(0 1)] + [else + (for/fold ([wins '(0 0)]) ([roll (in-list roll-space)]) + (define move-to (add1 (modulo (sub1 (+ roll p1-start)) 10))) + (define possible-wins (next-turns p2-score (+ p1-score move-to) p2-start move-to)) + (list (+ (first wins) (second possible-wins)) (+ (second wins) (first possible-wins))))])) + +(~>> (next-turns 0 0 player-1-start player-2-start) (apply max)) diff --git a/racket/aoc2021/day-22/day-22.rkt b/racket/aoc2021/day-22/day-22.rkt new file mode 100644 index 0000000..1dc4211 --- /dev/null +++ b/racket/aoc2021/day-22/day-22.rkt @@ -0,0 +1,32 @@ +#lang racket +(require "../../jj-aoc.rkt" + threading) + +(struct step (instruction xs ys zs) #:transparent) + +;; part 1 +(define (clamped-range nmin nmax) + (in-inclusive-range (max (string->number nmin) -50) + (min (string->number nmax) 50))) + +(define steps + (for/list ([l (in-list (~> (open-day 22 2021) (port->lines)))]) + (~>> l + (regexp-match #px"(.+) x=(-?\\d+)..(-?\\d+),y=(-?\\d+)..(-?\\d+),z=(-?\\d+)..(-?\\d+)") + rest + (match _ [(list direction xmin xmax ymin ymax zmin zmax) + (step (string->symbol direction) + (clamped-range xmin xmax) + (clamped-range ymin ymax) + (clamped-range zmin zmax))])))) + +(~> (for*/fold ([cubes (hash)]) + ([s (in-list steps)] + [to (in-value (step-instruction s))] + [x (step-xs s)] + [y (step-ys s)] + [z (step-zs s)]) + (hash-set cubes (list x y z) to)) + hash-values + (count (curry equal? 'on) _)) + diff --git a/racket/aoc2021/day-25/day-25.rkt b/racket/aoc2021/day-25/day-25.rkt new file mode 100644 index 0000000..7a3a5ca --- /dev/null +++ b/racket/aoc2021/day-25/day-25.rkt @@ -0,0 +1,35 @@ +#lang racket +(require "../../jj-aoc.rkt" + threading) + +(define sea-floor + (for*/hash ([(row i) (in-indexed (in-lines (open-day 25 2021)))] [(c j) (in-indexed row)]) + (values (list i j) c))) + +(define-values (max-i max-j) + (~> sea-floor hash-keys (argmax (λ (coord) (apply + coord)) _) (apply values _))) + +(define (cucumber-movement h c delta-i delta-j) + (define new-hash (hash-copy h)) + (for* ([(coord x) (in-hash h)] #:when (eq? x c)) + (match-define (list i j) coord) + (define neighbor (list (+ delta-i i) (+ delta-j j))) + (define neighbor-or-wrap + (if (hash-has-key? h neighbor) neighbor (list (if (= delta-i 0) i 0) (if (= delta-j 0) j 0)))) + (when (eq? #\. (hash-ref h neighbor-or-wrap)) + (hash-set*! new-hash coord #\. neighbor-or-wrap c))) + new-hash) + +(define (eastwards-movement h) + (cucumber-movement h #\> 0 1)) + +(define (southwards-movement h) + (cucumber-movement h #\v 1 0)) + +;; part 1 +(for/fold ([f sea-floor] [step 0] #:result (add1 step)) ([i (in-naturals 1)]) + (define f* (~> f eastwards-movement southwards-movement)) + #:break (equal? f* f) + (values f* i)) + +;; no part 2 -- merry Christmas diff --git a/racket/aoc2022/commentary.md b/racket/aoc2022/commentary.md new file mode 100644 index 0000000..8616464 --- /dev/null +++ b/racket/aoc2022/commentary.md @@ -0,0 +1,35 @@ +# Reflections on Advent of Code 2022 from a guy who kinda knows Racket + +I've gotten into the habit of doing [Advent of Code](https://adventofcode.com/), the annual puzzle-a-day programming challenge. There's a competitive aspect to it, but I'm mostly interested in it as a way to challenge myself and get exposed to algorithms and concepts I haven't seen before. I'm not a programmer by trade; I learned the basics of C in college and a little bit of numerical methods in grad school, but most of what I've learned since then is self-taught as a hobby, so structured programming challenges like this are a good way for me to gauge my skill level and see how CS theory is put into practice. + +The language I'm most comfortable with is [Racket](https://racket-lang.org/), a general-purpose high-level language in the Scheme family, so it's what I used this year for Advent of Code. In my experience, Racket is pretty well-suited for the kinds of problems you find in programming challenges; there hasn't been any problem yet where I've felt like trying to solve it in Racket has been a handicap. For the first few days I used [Jupyter Notebook with the IRacket kernel](https://docs.racket-lang.org/iracket/index.html) to annotate my answers, but I eventually gave up on that because it ended up being harder to debug. + +So, here's my day-by-day opinion on how this year went for me. + +* **[Day 1](https://github.com/hunkyjimpjorps/AdventOfCode/blob/main/aoc2022/day-01/day-01.rkt)**. *Parse a list of lists and return the top three sums.* This is the kind of problem Racket feels made for -- the code is basically a direct declarative translation of the problem statement. +* **[Day 2](https://github.com/hunkyjimpjorps/AdventOfCode/blob/main/aoc2022/day-02/day-02.ipynb)**. *Rock-paper-scissors strategy.* Lots of pattern-matching; the hardest part was probably figuring out the rules of the tournament. +* **[Day 3](https://github.com/hunkyjimpjorps/AdventOfCode/blob/main/aoc2022/day-03/day-03.ipynb)**. *Find common items in rucksacks.* Another problem well-suited to declarative style. +* **[Day 4](https://github.com/hunkyjimpjorps/AdventOfCode/blob/main/aoc2022/day-04/day-04.ipynb)**. *Find overlapping and redundant ranges.* I used the [`rebellion`](https://docs.racket-lang.org/rebellion/index.html) library here for its [range](https://docs.racket-lang.org/rebellion/Ranges.html) data type, which takes care of most of the problem by itself. AoC is a good opportunity to explore a language's ecosystem. +* **[Day 5](https://github.com/hunkyjimpjorps/AdventOfCode/blob/main/aoc2022/day-05/day-05.ipynb)**. *Move stacks of boxes around.* Linked lists make first-in, last-out stack problems like this pretty easy. The major difficulty here was actually parsing the input (and it was probably the hardest input to parse out of all the problems), but once that was resolved it was smooth sailing. +* **[Day 6](https://github.com/hunkyjimpjorps/AdventOfCode/blob/main/aoc2022/day-06/day-06.rkt)**. *Find chunks of unique characters in a string.* Another one that Racket handles without difficulty -- the entire problem statement can be represented as one `for` comprehension. +* **[Day 7](https://github.com/hunkyjimpjorps/AdventOfCode/blob/main/aoc2022/day-07/day-07.rkt)**. *Parse terminal output and calculate directory sizes.* The first speedbump of the year, and probably the first day where there's a rift between "do it right" and "just get the answer". I saw a lot of solutions that properly built the file hierarchy and walked the tree to calculate directory sizes, while I just made a hashtable of absolute paths and the sizes of everything within them. +* **[Day 8](https://github.com/hunkyjimpjorps/AdventOfCode/blob/main/aoc2022/day-08/day-08.rkt)**. *Scout a forest for the treehouse site with the best visibility.* A traditional AoC "search a grid of integers for something" problem, and the first one of the year where I represented a grid as a hashtable of coordinates. Racket doesn't have great multidimensional data structures built in, but rolling my own sparse matrix representation is pretty painless. +* **[Day 9](https://github.com/hunkyjimpjorps/AdventOfCode/blob/main/aoc2022/day-09/day-09.rkt)**. *Simulate a rope.* Simulation problems are my favorite kind of AoC problem (probably because they're the most similar to the kind of stuff I did in school). The solution I wrote for a one-segment rope in part 1 just needed to be folded over a list to give me a solution for the ten-segment rope in part 2. Higher-order functions are nice. +* **[Day 10](https://github.com/hunkyjimpjorps/AdventOfCode/blob/main/aoc2022/day-10/day-10.rkt)**. *The traditional fake-ASM parsing problem*, and another problem that's just largely just folding a function over an accumulator. I feel like if you get comfortable with `for/fold` you can get about 25 AoC stars right off the bat. +* **[Day 11](https://github.com/hunkyjimpjorps/AdventOfCode/blob/main/aoc2022/day-11/day-11.rkt)**. *Monkeys take your stuff.* This problem features my least favorite AoC trope, which is a Part 2 that requires you to know a particular fact about modular arithmetic to solve it in a reasonable amount of time. It also had a data file I found less annoying to retype by hand than to parse. Probably the worst one of the set. +* **[Day 12](https://github.com/hunkyjimpjorps/AdventOfCode/blob/main/aoc2022/day-12/day-12.rkt)**. *Find a hiking route up a spiral mountain.* I leaned pretty hard on the [`graph`](https://docs.racket-lang.org/graph/index.html) package for this one. Eventually I'd like to learn how to implement Dijkstra and A* and other graph traversal algorithms myself, but for now I'm satisfied with just figuring out how to use the prepackaged versions. +* **[Day 13](https://github.com/hunkyjimpjorps/AdventOfCode/blob/main/aoc2022/day-13/day-13.rkt)**. *Sort a recursive data type.* I'm really glad for two features of Racket for this one. First, the ability to read data as code, which meant I could read the nested lists directly with just one tweak to the readtable and without doing any string parsing at all. Second, structural pattern matching. Racket's matching syntax isn't as elegant as some languages (I miss the `[x | xs]` form from Elixir compared to `(list* x xs)`, for example), but it's powerful. +* **[Day 14](https://github.com/hunkyjimpjorps/AdventOfCode/blob/main/aoc2022/day-14/day-14.rkt)**. *Simulate falling sand.* A fun simulation problem, and one that both has a naive solution that solves in a reasonable amount of time and an optimized backtracking solution that's easy to reason out. This was my favorite puzzle of the set. +* **[Day 15](https://github.com/hunkyjimpjorps/AdventOfCode/blob/main/aoc2022/day-15/day-15.rkt)**. *Find the only possible place for a missing beacon.* The fundamental problem here is finding out a way to represent a range of integers sparsely (or [using a library that does that for you](https://docs.racket-lang.org/data/integer-set.html)). Lots of opportunities for optimization. +* **[Day 16](https://github.com/hunkyjimpjorps/AdventOfCode/blob/main/aoc2022/day-16/day-16.rkt)**. *Teach an elephant to open valves*. The first of a few heuristic search problems with enormous solution spaces. Part 1 (one person opening valves) is OK, but I only eventually solved Part 2 (two people simultaneously opening valves) a week later by watching console output and making a reasonable guess. [Nearly half of the remaining participants in AoC quit at this point](https://adventofcode.com/aoc2022/stats) and I don't really blame them. +* **Day 17** (no solution). *Find the period in the pattern of falling rocks.* I'm sure this one isn't too bad, but I was feeling too burnt out by Day 16 to give it more than a token try. +* **[Day 18](https://github.com/hunkyjimpjorps/AdventOfCode/blob/main/aoc2022/day-18/day-18.rkt)**. *Find the surface area of an irregular rock.* A nice straightforward volume-scanning problem, and another opportunity to use my sparse matrix data type. +* **Day 19** (no solution). *Pick the best plan for building mining robots.* Another heuristic search problem, so I skipped this one since I was still failing at day 16 part 2. Picking out an optimized robot building strategy had more moving parts than I knew how to deal with. +* **[Day 20](https://github.com/hunkyjimpjorps/AdventOfCode/blob/main/aoc2022/day-20/day-20.rkt)**. *Repeatedly shift elements around in a circular array.* Singly-linked lists are terrible for repeated arbitrary access, but I don't care. I figured out an algorithm that worked and I was happy enough with that to accept it and move on. After the previous streak of problems, I was just happy to figure out Part 2 on my own. (There was a modulo math trick element to Part 2 here as well, but this one was a lot more obvious to spot.) +* **[Day 21](https://github.com/hunkyjimpjorps/AdventOfCode/blob/main/aoc2022/day-21/day-21.rkt)**. *Figure out what to shout in the monkeys' math game.* I have a feeling the intended solution for Part 2 of this one was to build the expression tree and backtrack through the operations to calculate the unknown value. However, I just blindly started evaluating the result for various guesses and discovered through trial and error that the relationship between the guess and the result is linear, so all you need for part 2 is two guesses, their corresponding results, and some algebra. It's fun to accidentally discover simple solutions to complex-looking problems. +* **Day 22** (no solution). *Something involving walking around on a map.* I skipped this one because of the flu. C'est la vie. +* **[Day 23](https://github.com/hunkyjimpjorps/AdventOfCode/blob/main/aoc2022/day-23/day-23.rkt)**. *Cellular elfomata.* Another AoC staple, though later than usual. I think the story got in the way of the problem description a little bit here, but once I talked through the rules with someone else it was a straight shot to the solution. +* **Day 24** (no solution). *Walk through a blizzard.* I had to skip this one because of holiday travel (ironically made more difficult by a blizzard) and family obligations. It looks interesting, though, so I'm hoping to have some time to come back to it before January. +* **[Day 25](https://github.com/hunkyjimpjorps/AdventOfCode/blob/main/aoc2022/day-25/day-25.rkt)**. *Figure out balanced penternary.* The final AoC problem is always a clever little math brain teaser with one part, and this one was fun to puzzle out with pencil and paper. + +I think Days 16 and 19 could've used a second pass to narrow the possible solution space, or at least make the best heuristic a little more straightforward to reason out, but on the balance, I enjoyed myself this year, and I'm generally happy with my solutions and implementations. \ No newline at end of file diff --git a/racket/aoc2022/day-01/day-01.ipynb b/racket/aoc2022/day-01/day-01.ipynb new file mode 100644 index 0000000..c79a3f6 --- /dev/null +++ b/racket/aoc2022/day-01/day-01.ipynb @@ -0,0 +1,139 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Advent of Code 2022\n", + "#### Day 1: Calorie Counting\n", + "\n", + "Elves carry various amounts of food with various caloric contents.\n", + "\n", + "**Part 1.** How many calories is the elf with the most calories of food carrying?\n", + "\n", + "**Part 2.** How many calories are the three elves with the most calories of food carrying?" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "(require racket\n", + " advent-of-code\n", + " threading)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### Part 1\n", + "\n", + "The data file is a list of integers, one on each line, with an empty line separating the inventory of each elf.\n", + "\n", + "1. Fetch the input file,\n", + "2. split it on double newlines to find each elf's inventory,\n", + "3. split each inventory on single newlines,\n", + "4. convert each inventory member to a number,\n", + "5. sum up each list, and\n", + "6. find the maximum one. \n", + "\n", + "This is straightforward to do with threading/piping:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "70374" + ], + "text/plain": [ + "70374" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(define calorie-data (fetch-aoc-input (find-session) 2022 1))\n", + "\n", + "(~>> calorie-data\n", + " (string-split _ \"\\n\\n\")\n", + " (map (λ~>> string-split (map string->number) (apply +)))\n", + " (apply max))\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### Part 2\n", + "\n", + "Similarly, to find the top three calorie holders,\n", + "\n", + "1. fetch the input file,\n", + "2. split it on double newlines,\n", + "3. split each list member on single newlines,\n", + "4. convert each sublist member to a number,\n", + "5. sum up each list, \n", + "6. sort the list from high to low,\n", + "7. take the first three members,\n", + "8. and sum them." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "204610" + ], + "text/plain": [ + "204610" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(~>> calorie-data\n", + " (string-split _ \"\\n\\n\")\n", + " (map (λ~>> string-split (map string->number) (apply +)))\n", + " (sort _ >)\n", + " (take _ 3)\n", + " (apply +))\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Racket (trusted)", + "language": "racket", + "name": "racket-trusted" + }, + "language_info": { + "codemirror_mode": "scheme", + "file_extension": ".rkt", + "mimetype": "text/x-racket", + "name": "racket", + "pygments_lexer": "racket", + "version": "8.7" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/racket/aoc2022/day-01/day-01.rkt b/racket/aoc2022/day-01/day-01.rkt new file mode 100644 index 0000000..5215014 --- /dev/null +++ b/racket/aoc2022/day-01/day-01.rkt @@ -0,0 +1,20 @@ +#lang racket + +(require advent-of-code + threading) + +(define calorie-data (fetch-aoc-input (find-session) 2022 1)) + +;; part 1 +(~>> calorie-data + (string-split _ "\n\n") + (map (λ~>> string-split (map string->number) (apply +))) + (apply max)) + +;; part 2 +(~>> calorie-data + (string-split _ "\n\n") + (map (λ~>> string-split (map string->number) (apply +))) + (sort _ >) + (take _ 3) + (apply +)) diff --git a/racket/aoc2022/day-02/day-02.ipynb b/racket/aoc2022/day-02/day-02.ipynb new file mode 100644 index 0000000..13b9986 --- /dev/null +++ b/racket/aoc2022/day-02/day-02.ipynb @@ -0,0 +1,193 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Advent of Code 2022\n", + "#### Day 2: Rock Paper Scissors\n", + "\n", + "You've given a strategy guide for how to win at a Rock Paper Scissors tournament. The first column is what your opponent will throw. Your score is determined by the result (win, lose, draw) of each round and what you played (rock, paper, scissors).\n", + "\n", + "**Part 1.** What's your tournament score if the second column represents what you should play in each round?\n", + "\n", + "**Part 2.** What's your tournament score if the second column represents the result of each round?\n", + "\n", + "For this solution, I'm using `rackjure` since I'm planning on using a bunch of dictionaries, and `rackjure`'s shorthand makes them easier to work with." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "vscode": { + "languageId": "racket" + } + }, + "outputs": [], + "source": [ + "#lang iracket/lang #:require rackjure\n", + "(require advent-of-code)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The input for this problem is a list with two columns; the first column is one of the characters `A`, `B` or `C` (corresponding to the opponent's throw of rock, paper or scissors) and the second column is `X`, `Y` or `Z`, whose meaning is currently unknown." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "vscode": { + "languageId": "racket" + } + }, + "outputs": [], + "source": [ + "(define strategy-guide (~> (fetch-aoc-input (find-session) 2022 2) (string-split \"\\n\")))\n", + "(define opponent-throw {\"A\" 'rock \"B\" 'paper \"C\" 'scissors})" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We're also given the score for a round result and the bonus for the selected throw, and we write a function that determines the result for a given round." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "vscode": { + "languageId": "racket" + } + }, + "outputs": [], + "source": [ + "(define score-bonus {'rock 1 'paper 2 'scissors 3 'win 6 'draw 3 'lose 0})\n", + "\n", + "(define winning-rounds {'rock 'paper 'paper 'scissors 'scissors 'rock})\n", + "(define losing-rounds {'rock 'scissors 'paper 'rock 'scissors 'paper})\n", + "\n", + "(define (outcome them me)\n", + " (match* (them me)\n", + " [(x x) 'draw]\n", + " [(x y) #:when (eq? y (winning-rounds x)) 'win]\n", + " [(_ _) 'lose]))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### Part 1\n", + "\n", + "In part 1, we assume that the second column refers to the throw we should select in each round. We add that to our existing dictionary and write a `for` comprehension to calculate each round result." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "vscode": { + "languageId": "racket" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "13809" + ], + "text/plain": [ + "13809" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(define assume-throw (dict-merge opponent-throw {\"X\" 'rock \"Y\" 'paper \"Z\" 'scissors}))\n", + "\n", + "(for/sum ([play (in-list strategy-guide)])\n", + " (match-define (list them me) (string-split play))\n", + " (+ (score-bonus (outcome (assume-throw them) (assume-throw me)))\n", + " (score-bonus (assume-throw me))))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### Part 2\n", + "Now we're told that the second column actually represents the round result: `X` is lose, `Y` is draw, `Z` is win. We can look up what we should throw in response for each round, and then calculate the score from that." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "vscode": { + "languageId": "racket" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "12316" + ], + "text/plain": [ + "12316" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(define assume-result (dict-merge opponent-throw {\"X\" 'lose \"Y\" 'draw \"Z\" 'win}))\n", + "(define (pick-throw them result)\n", + " (match* (them result)\n", + " [(x 'draw) x]\n", + " [(x 'win) (winning-rounds x)]\n", + " [(x 'lose) (losing-rounds x)]))\n", + "\n", + "(for/sum ([play (in-list strategy-guide)])\n", + " (match-define (list them result) (string-split play))\n", + " (+ (score-bonus (assume-result result))\n", + " (score-bonus (pick-throw (assume-result them) (assume-result result)))))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Racket (trusted)", + "language": "racket", + "name": "racket-trusted" + }, + "language_info": { + "codemirror_mode": "scheme", + "file_extension": ".rkt", + "mimetype": "text/x-racket", + "name": "Racket", + "pygments_lexer": "racket", + "version": "8.7" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "916dbcbb3f70747c44a77c7bcd40155683ae19c65e1c03b4aa3499c5328201f1" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/racket/aoc2022/day-02/day-02.pl b/racket/aoc2022/day-02/day-02.pl new file mode 100644 index 0000000..707da41 --- /dev/null +++ b/racket/aoc2022/day-02/day-02.pl @@ -0,0 +1,72 @@ +:- use_module(library(yall)). +:- use_module(library(apply)). + +output_solutions :- + get_data(Games), + part1_total(Games, Part1), + write(Part1), nl, !, + part2_total(Games, Part2), + write(Part2), nl. + +% Facts + +game(X, X, draw). +game(rock, scissors, win). +game(scissors, paper, win). +game(paper, rock, win). +game(rock, paper, lose). +game(scissors, rock, lose). +game(paper, scissors, lose). + +opponent_move("A", rock). +opponent_move("B", paper). +opponent_move("C", scissors). + +assume_move("X", rock). +assume_move("Y", paper). +assume_move("Z", scissors). + +assume_outcome("X", lose). +assume_outcome("Y", draw). +assume_outcome("Z", win). + +bonus(rock, 1). +bonus(paper, 2). +bonus(scissors, 3). +bonus(lose, 0). +bonus(draw, 3). +bonus(win, 6). + +% Rules + +get_data(Result) :- + setup_call_cleanup(open("2022/day-02/prolog-input.txt", read, In), + (read_string(In, _, Str), + split_string(Str, "\n", "\s\t\n", Lines), + maplist([In, Out] >> split_string(In, "\s", "", Out), Lines, Result)), + close(In)). + +score_game(MyMove, Result, Score) :- + bonus(Result, X), + bonus(MyMove, Y), + Score is X + Y. + +part1_score([Them, Me], Score) :- + opponent_move(Them, TheirMove), + assume_move(Me, MyMove), + game(MyMove, TheirMove, Result), + score_game(MyMove, Result, Score). + +part1_total(Games, Total) :- + maplist(part1_score, Games, Scores), + sum_list(Scores, Total). + +part2_score([Them, Outcome], Score) :- + opponent_move(Them, TheirMove), + assume_outcome(Outcome, Result), + game(MyMove, TheirMove, Result), + score_game(MyMove, Result, Score). + +part2_total(Games, Total) :- + maplist(part2_score, Games, Scores), + sum_list(Scores, Total). \ No newline at end of file diff --git a/racket/aoc2022/day-02/prolog-input.txt b/racket/aoc2022/day-02/prolog-input.txt new file mode 100644 index 0000000..95a2b2a --- /dev/null +++ b/racket/aoc2022/day-02/prolog-input.txt @@ -0,0 +1,2500 @@ +C X +C X +A Y +C X +B Y +A X +A Z +B Y +C Z +C Z +B X +C Z +B Y +C Z +B Y +A Z +B Y +C X +C X +C X +B X +C Z +C X +C Z +C X +A Y +B Y +B Z +A X +C X +C Z +C Z +A Z +B Y +C Z +C X +C X +C Z +B Y +C Z +C Z +C X +B X +B X +A Y +C Z +C Z +B Y +B Y +C Z +C X +A Z +C X +C Z +C Z +B X +C Z +C Z +B Y +A Y +B X +C X +C X +C Z +C X +A Y +C X +C Z +A Z +B Y +C Z +C X +C X +C Z +C Z +C Z +A Z +C Z +A Z +A Z +C X +B Y +C X +C X +A Z +C X +A Y +C Z +C Z +A Y +A Z +C Z +C Z +A Y +C Z +C Y +B Y +B Y +A Z +A Y +C Z +C Z +A Z +C Z +A Y +B Z +C X +C Z +C X +B Y +C Z +C X +B Y +B X +A Y +C Z +C Z +C Z +B Z +A Y +C Z +C Z +C X +C Z +C X +A Y +C Z +C Z +C X +B Y +B X +B Y +C Z +C X +B X +C Z +C Z +A Y +C Z +A Z +A Y +C Z +A Y +C X +A Y +C Y +A Z +C X +C X +B Y +B Y +A Y +A Z +C Z +C X +C Z +A Y +B Y +A Y +B X +C Z +C Z +A Z +A Y +C X +C X +C X +A Z +B Y +C Z +C Z +C Z +C X +B Z +C Z +C Z +B Z +C Z +A Z +B Y +A Y +C Z +B Y +B X +B X +C X +C Z +A X +C Z +C X +C X +C X +C X +C X +B Y +C X +C X +A Y +A Y +C Z +C Z +B X +C Z +C Z +C Z +C Z +A Z +C Z +A Z +B Y +B Y +C Z +B Y +C Z +C Z +C Z +C X +B Y +B Y +C Z +A Z +C Z +C X +B Y +C Z +A Z +C Z +C X +C Z +C Z +C X +C X +B Z +A Y +B X +C Z +B Y +C X +C X +C Z +C Z +B X +B X +B Y +A X +C X +A Z +A Z +C Z +B Y +C Z +A Z +B X +C X +B Y +A Z +C X +A X +A Z +B Y +B X +B Y +A Y +C Z +C Z +B X +C Z +C X +B Y +C Z +C Z +A Y +B Y +A Z +A Y +C X +C X +C Z +A Z +C Z +B Z +A Z +A Y +C X +C Z +C X +B Z +C Z +B Y +A Y +B X +A Y +C Z +A Y +C Z +C X +B Y +C X +A Y +A Z +C Z +B Y +C X +A Y +C X +C Z +C X +B Y +C X +C Z +C Z +A Z +B Y +C X +B X +A Z +C X +C X +A Y +B X +C X +A Z +C Z +C Z +C Z +B Y +A Y +C X +C Z +C Z +A Z +C Z +A Y +C X +C X +C X +A Z +B Y +C Z +A Y +C Z +C Z +C X +C Z +A Z +C Z +B Y +C X +C X +C Z +B Z +B Y +C X +C X +A Y +A Z +A Z +A X +C X +A Y +B Y +A Y +A Z +B Y +B Y +A Y +B Y +C X +A Z +B X +C Z +A Z +B X +A Y +B X +B Y +A Y +A X +C Z +B Z +B X +B Z +C Z +C X +B X +B Y +A Y +B Y +B Y +B Y +A Z +A Y +B X +A Y +C X +B Y +B X +B Y +C X +A Y +C X +A Y +C Z +C Z +A Z +C Z +C X +C X +A Z +C X +C X +C X +A Y +A Z +A Z +C Z +B X +C X +C X +C Z +A Y +C X +C X +B X +C Z +C Z +C X +B Z +C X +C X +C Z +A Y +C X +A Z +C Z +C X +B X +B Y +C X +C Z +C X +C Z +A Z +C Z +C X +C Z +C Z +A Z +B X +C X +C Z +C X +C X +C Z +C X +C Z +A Z +A Z +A Z +C Z +C X +A Z +C Z +C Z +C Z +A Z +B Y +C X +B Y +C X +C Z +B Y +C X +C X +A Z +A Z +C X +C Z +C X +C Z +A Z +A Y +C Z +C Z +A Y +B Y +B Y +C Z +B Y +B X +B Z +A Y +A Z +C X +B Y +B Z +B Y +B Z +C Z +B Y +C Z +C Z +B Y +B X +B Z +C X +A Z +C X +C X +C X +A Z +C Z +A Z +C Z +A Y +C Y +B Y +A Z +B Y +C Z +A Z +A Y +B X +C X +C X +C X +C Z +C X +B X +C Z +A Y +C Z +A X +B Y +B Z +C Z +B Y +C Z +B X +B Y +C Z +B X +A Z +B X +B Y +A Y +B Z +C X +C Z +A Z +A Y +A Z +C X +C Z +B Z +A Z +A Z +C X +C Z +C X +C X +A Z +C X +C X +C X +B X +A Y +B Y +B X +B Y +C Z +B Y +B Z +C Z +C X +B Y +C Z +C Z +C Z +C Z +C Z +C Z +A Z +A Z +A Z +C X +C Z +B Y +C Z +C Z +C Z +C Z +C X +C X +A Z +B Y +A Z +B Z +C X +C X +C Z +C Z +C X +A Y +C X +A Y +C Z +A Z +C Z +B X +C Z +C Z +C X +C X +C Z +B Z +A Y +B X +A Y +B Y +A Y +A Z +A Z +C Z +B X +C Z +C X +C Z +C Z +A Z +C X +A Y +C X +A Z +C Z +C X +C Z +A Z +C X +C X +C X +C X +B X +B Y +C X +C Z +C Z +C Z +C Z +A Z +A Z +A Y +C Z +C X +C Z +C Z +C Z +A Y +C X +A Z +C Y +A Z +C Z +C X +A Y +C Z +C X +C X +A Z +B Y +C Z +A Z +C Z +C Z +C Z +B Y +C X +C X +A X +A Y +C Z +A Z +C Z +B Y +C X +B X +C X +C X +A Y +C Z +C Z +C Z +C X +A Z +B Y +A Y +B Z +B Z +B X +A Z +B X +B X +A Z +A Z +C Y +B Y +C Z +A X +C Z +B X +C Z +A Y +A Y +C Z +C Z +A Z +B Y +C Z +C Z +C Z +C X +A Y +C X +B Y +B Y +C X +C Z +C X +B X +A Y +A Y +C Z +C Z +C Z +C Z +C Z +B X +C Z +A Y +B X +A Y +A Z +C Z +C X +B Y +B Y +C Z +C Z +C Z +B Y +C X +B Y +A Y +B X +C Z +C Z +A Y +C X +C Z +A Y +C X +C X +A Z +C Z +B X +A Z +B Y +C Z +A Z +B X +C Z +B Y +C Z +B Y +C X +C Z +B X +C X +B Y +C X +A Y +C Z +C Z +C X +B Y +C X +C Z +C X +C Z +A Z +A Y +C X +C Z +C Z +A Z +C X +B Z +A Z +B Y +C X +A Z +C Z +B Y +C X +C X +C Z +C Z +B Y +A Z +C Z +C X +C Z +A Z +C X +A Z +C Z +C X +C X +C Z +B X +C Z +C Z +C Z +B Y +A X +B Y +C X +A Z +B X +A Z +C Z +C X +C Z +C Z +B Y +B X +C Z +B Z +B Y +B X +C X +C X +C X +C Z +B Y +C Z +C Z +C Z +C Z +C Z +C Z +C X +C Z +A Y +C X +B X +A Y +C X +C X +C Z +C Z +C X +B X +B Y +B Y +C Z +B X +C X +C X +C Z +C Z +A Y +C Z +A Y +C Z +C Z +B Y +A Z +B X +B X +C Z +C Z +A Y +A Y +C Z +C Z +C X +A Y +A Y +C Z +A Z +C X +B Z +A Y +C X +B Z +A Y +C X +B Y +C X +C X +C Z +A X +C Y +A Y +B Z +B Y +A X +B Y +A Z +C Z +C X +C X +C X +C Y +B Y +C Z +A Z +C X +C X +C Z +C Z +C X +C Z +B Y +C Z +C X +B Y +A Z +C X +B Y +C Z +C X +B Y +A Z +B X +C Z +B X +B X +C Z +C Z +C X +B Y +A Y +B X +B X +A Y +B Y +B Y +B X +A Z +A Y +C Z +B Y +C Z +A Z +C Z +C X +A Z +C X +C Z +C X +B Y +C X +A Z +B X +C Z +C X +C X +B Y +A Y +C Z +C X +A Y +A X +C Z +B Y +B X +C X +C X +C X +C Z +A Z +B Y +A Y +B Y +B X +B Y +B Y +A Z +B Y +B Y +B Y +C Z +C X +A Z +A X +B Z +C X +C X +C X +C X +C Z +A Z +B Y +A Z +B Y +C X +A Z +A Y +C Z +C X +B X +A Z +B Y +C Z +A Z +C X +A Z +A Y +B Y +C Z +B Y +C X +A Z +A Z +A X +C Z +C X +A Y +B Y +B X +C Z +A Z +C X +B X +B Y +A Z +C X +B Y +C X +C Z +C Z +B Y +A Z +A Y +C X +B Y +C X +C X +C Z +C Z +C X +A X +C X +A Z +C Z +B X +B X +C X +B X +B Y +C X +C X +A Y +B Y +C Z +C Z +C Z +C Z +C Z +C Z +A Z +B X +C X +B Y +B Y +C X +C Z +A Z +C X +C Z +C Z +C X +A Z +C Z +A Z +C Z +A Y +C X +C X +B Y +C X +C Z +B X +A Y +C Z +C X +B Y +B X +A Y +C X +A Z +C Z +C X +C X +A Y +B Y +C Z +B Z +C Z +C X +B Y +C Z +C X +A Y +C X +C Z +C X +C X +B X +C X +B Y +C X +C Z +C X +B X +B X +C Z +A Y +C Z +C Y +B X +A Z +C X +A Z +B Z +A Y +C Z +C Z +A Z +B Z +A Z +C X +C Z +C Z +A Z +A Y +C Z +C X +A Y +B Y +B X +A Z +A Y +C X +B X +A Y +C Z +B Y +C X +C X +C X +B X +A Z +B Y +B Y +A Z +C X +B X +B X +A Y +C Z +C X +C X +A Y +C X +C X +C Z +C X +A Y +B Y +C Z +A Z +C Z +A Z +A Z +A Y +C X +C X +C X +A Y +A Y +B Y +B Z +A Z +C X +C Z +C X +C Z +B X +C X +C X +B X +C Z +C X +B Y +B X +C Z +A Z +C Z +B Y +C Z +C Z +B X +A Y +B Y +A Z +B Y +C X +C X +A Z +C Z +C X +C Z +C X +A Z +C X +A Z +C X +A Y +A Z +C X +C Z +B Y +C Z +A Z +C Y +B Z +B Y +A Z +C Z +A X +A Z +C Z +C X +C X +A Y +C Z +C X +C Z +C Z +C Z +B Y +C Z +C X +C Z +B Y +C Z +B Y +C Z +C Z +C Z +C Z +C Z +B Y +C Z +B Y +A Y +C X +B Y +A Y +C X +A Z +A Y +C Z +B Y +C Z +C X +A Y +B Y +C X +C X +C X +A Y +A Z +B X +B X +B X +B Y +C Z +B Y +C Z +C Z +B Y +A X +C X +A Y +C Z +B Y +B Y +C Z +C Z +C X +C Z +C Z +B Y +C Z +C X +A Y +A Z +C X +B Z +C X +B Y +C Z +C X +A Y +A Z +C Z +C X +C Z +C X +C X +C X +C X +C Z +C X +B Y +A Y +C X +C Z +A Z +B Y +C Z +C X +C Z +B Y +A Z +A Y +A Y +B Y +B Y +A Z +B X +A Y +C X +C X +A Y +C X +A Y +B Y +C Z +A X +B X +A Y +A Z +C Z +B X +C Z +B Y +C X +B Y +C Z +A Z +A Y +C X +C Z +B X +B Y +C Z +C Z +A Z +C Z +B Z +C X +C X +C Z +C Z +C Z +C X +C X +C Z +B Y +C Z +B Z +C X +A Y +C Z +B Y +C X +A Y +C Z +B X +C Z +A Z +C Z +C X +A Y +A Y +A Y +C Z +C Z +A Y +A X +C Z +C Z +C X +C Z +A Z +A Z +C Z +C X +C Z +B Y +A Y +B Z +B Y +C X +C Z +C X +B X +B Y +C X +C X +C X +A Z +A Y +C X +C Z +C X +B Y +C Z +B X +C Z +B Y +A Z +C X +B Y +C X +B X +A Z +C X +B Y +A Y +C Z +C X +A Y +C X +B Y +B X +C Z +C X +C X +C Z +C X +C X +A Y +A X +C Z +C Z +C Z +B Y +C Z +A Z +B X +C Z +C X +C X +A Y +A Z +B Y +B Y +C Z +C Z +A Y +A Z +A Z +C Z +B Y +B Y +C Z +B Y +C X +C X +C Z +A Z +C X +C X +B Y +B Y +C Z +B Y +C Z +C Z +C Z +C Z +C X +C Z +B Y +C Z +C Z +B X +C Z +C Z +C X +B Y +C Z +A Z +A Z +A Z +C X +B Y +C Z +A Y +B X +C X +C X +A X +A Y +B X +C Z +C X +C Z +C Z +B Y +C Z +C Z +B Z +C Z +C Z +B X +A Y +C X +A Z +B Y +A Z +C X +B X +B X +C Z +A X +B Z +A Z +B Y +C X +C Z +B Z +C X +B X +C Z +A Z +B X +C Z +C Z +A Z +A Y +C Z +C Z +B X +A Z +C Z +B Y +B Y +C Z +B Y +C Z +C Z +C Z +C Z +C Z +C Z +C Z +C X +A Y +C Z +C Z +C X +C Z +B Y +B X +B Y +A Z +C Z +B Y +B X +C Z +B X +C Y +C X +B Y +C Z +B Y +A Z +B Y +C X +C Z +B X +C Z +A Y +C Z +B X +A Z +A Y +B Y +C Z +A Y +B Y +C X +A Z +A Y +C Z +C Z +A Y +B X +C X +B Y +A Y +C X +B X +C X +C Z +C Z +B Y +A Z +B Y +A Z +A Y +A Z +B X +A Y +C X +B Y +C Z +C Z +A Z +C Z +C Z +C X +C Z +C Z +B Y +A Z +C X +A Y +C Z +A X +A Z +C Z +B Y +C X +C Y +A Y +B X +B Y +C Z +C Z +B X +B X +C Y +B Y +B X +C X +C Z +A Z +C Z +A Z +C Z +C Z +A Z +B X +C Z +C X +C Z +B Y +B X +A Z +C Z +B X +C Z +B Z +C Z +B Y +B Y +C Z +B Y +A Y +A Z +A Z +C X +A X +C X +C Z +C Z +A Y +C Z +C Z +C Z +A Y +B X +C Z +C X +B X +C X +C X +B Y +C Z +B X +C X +B Y +A Y +C Z +C X +C X +A Z +B Y +C Z +B Y +C Z +B Z +B Y +B Y +A Y +B Y +B Y +A X +A Y +C Z +C X +B X +C Z +C X +C X +B Y +C Z +C Z +B Y +A Y +B Y +C Z +C X +C Z +C Z +C X +A Y +A Y +C Z +A Y +C X +C Z +A Z +A Z +C Z +B Y +A X +A X +B X +A Z +C X +C X +C X +C X +A Z +A Z +C X +B X +B Y +C X +B Y +B Z +A Z +A Y +C X +B Y +B Y +C Z +B X +B X +C Z +B Y +C Z +C Z +B Y +C Z +C X +C Z +B Y +C X +C Z +C Z +C X +B X +C Z +C X +C X +C Y +C Z +B Y +C X +C X +A Y +C X +C X +C Z +C X +C Z +C Z +C Z +A X +C Z +C Y +C Z +C Z +C Z +C Z +A X +C Z +B X +C Z +C X +B Y +C X +A Z +C Z +C X +B Y +B Y +A Z +A Y +C X +A Z +C Z +C Z +A Y +A Z +A X +C Z +A Z +C Z +C Z +C Z +A Z +A Y +A Z +A Z +A Y +A Y +C X +A Y +A Y +C Z +C Z +C Z +B X +C X +C Z +B X +C Z +C Z +C Z +C Z +C Z +B Y +C Z +B Y +C Z +C X +A Z +C Z +B X +B Y +C X +B Y +C X +C X +A Y +C Z +C X +C X +C Z +C X +C X +C Z +B Y +B X +C X +B X +B Y +C X +A Z +B Y +A Z +B Y +A Z +A Y +C Z +C Z +C X +B Y +A Y +A Y +C X +B Y +C X +A Z +A Z +C X +A X +C X +A Z +C X +C Z +C X +A Y +C X +B Y +B Y +C X +A Z +C Z +C Z +B X +C Z +C Z +A Y +C Z +B Y +A Z +A X +C Z +C X +B Y +A Z +C X +B X +A Z +A Z +C Z +A Z +C Z +C Z +A X +C X +A Z +C X +A Y +B Y +C Z +B Y +B X +C Z +A X +B Z +A Z +A Z +C Z +A Z +C Z +A Z +C X +C X +C X +C X +A Z +C Z +C X +B Y +A Z +B Y +A Y +C Z +A Z +C X +C Z +B Y +C X +A Y +B X +A Y +B X +B Y +C Z +A Y +B Y +A Y +B Y +C Z +C X +C X +C X +C Z +B Y +C Z +B X +C X +A Y +A X +B Y +A Z +C Z +A Y +A Z +C X +B Y +A Z +C Z +C X +C Y +C X +A Z +A Y +C X +A Y +C X +C X +A Z +B Y +C Z +C X +B X +B Y +A Z +A Z +B X +B Y +C X +B Y +C Z +B Y +C Y +B Y +B X +B Y +A Y +B Y +C Z +B X +B Y +C Z +A Z +C Z +B X +A Y +B X +C Z +B Y +A Y +C Z +C Z +C Y +C Z +A Y +C X +A Y +C Z +C Z +C Z +C Z +C Z +C X +C Z +C Z +B Y +C X +C Z +B X +C X +C Z +C X +A Z +C X +C X +C Z +C X +C X +C Z +A Z +B X +B Y +C Z +A Z +C X +B X +B Y +C Z +C Z +A Y +C X +C Z +C X +C Z +B Y +C X +C Z +C X +C X +C Z +C X +B X +C X +C Z +A Y +A Y +A Y +C Z +C Z +B X +C X +A Z +C Z +C X +C Z +B X +C Z +B Z +C X +A Y +C Z +A Y +C X +B Y +B Y +A Z +C Z +C Z +C Z +A Z +C X +A Y +C Z +A Y +C Z +A Z +A Z +B Y +C Z +A Z +C X +A Y +C Z +C Z +C X +C X +C X +A X +B Z +C Z +C X +A Y +A Z +C Z +B X +B Y +C X +C Z +B X +B Y +C X +B X +C Z +C X +C X +C X +C X +A Y +A Z +A Y +B Y +C Z +B Y +B Y +C Z +A X +C Z +B X +C Z +C Z +A Z +B Y +C Z +C Z +A Y +A Z +A Z +A Z +B X +C X +C X +B Y +C Z +C X +B Y +A Y +C X +C Z +C Z +C X +C Y +A Y +A Z +C X +C X +A Z +B X +C Z +A X +C Z +C X +C Z +A X +A Z +C X +B Y +C Z +C Z +B X +A Y +B Y +C Z +C Z +A Z +A Y +C Z +C Z +A X +B Y +C Z +C Z +B Y +C X +C Z +B X +B Y +A Z +C Z +C X +C X +C Z +B Z +B Y +C Z +C X +A Y +C X +A Y +A Z +B Y +B X +C Z +A Z +C Z +C Z +C Z +A Y +B Y +A Z +B Y +C Z +C X +B Y +C Z +A Y +C Z +A Z +C Z +C Z +C Z +A Z +C X +B Y +C Z +A Z +C Z +C Z +A Z +C Z +C X +B Z +C Z +C Z +A Z +C Z +C Z +B Y +B X +C Z +C Z +B Y +A Y +C Z +A Z +A Z +C Z +C X +C X +B X +C X +A Y +B X +A Y +C Z +C X +B Y +C Z +C X +C X +C X +B X +B Z +C Z +B Y +C X +A Z +C Z +C Z +A Z +A Z +C Z +C Z +A Z +A Z +A Z +C Z +A Z +C Y +A Z +C Z +C Z +C X +C X +C Z +C Z +A Z +C Z +A Z +C X +C X +C X +A Y +A Z +A Z +A Y +B X +C Z +C Z +B X +C Z +C X +B Y +C Z +C X +C X +B Y +B Y +A Y +A Z +B X +C X +B Y +B Y +B Y +C Z +C X +C X +A Y +B Z +A Z +A Z +B X +C X +A Y +C X +C Z +C X +A Y +A Z +C Z +A Z +C Z +C X +C X +A Z +B X +B X +B Y +C Z +C Z +C X +C X +C X +B Y +C Z +C X +B X +C Z +C X +B X +A Y +B Y +C Z +A Z +A Y +C Z +A Y +A Y +C Z +B Y +C Z +B Z +C Z +A Z +C X +C X +C Z +B X +C X +B Y +C Z +B X +C Z +C X +B X +B Y +B Z +B X +A Y +C X +C Z +C Z +A Y +B X +A Y +C X +C Z +B Y +C Z +C Z +C Z +A Z +A Z +A Y +C Z +B Y +C Z +C X +B Y +C Z +C Z +A X +C X +B Y +A Z +C Z +C X +A Z +A Y +C Z +C Z +B X +C Z +A Z +A Y +C X +A Y +C Z +C X +C X +C X +B Y +C X +A Y +C X +B Y +B X +A Y +B X +A Y +B Z +C Z +A Z +B Y +B X +C Z +C Z +C X +B Y +A Z +A Z +A Y +A Y +C Z +A Z +A Z +B Y +C X +C Z +C Z +C Z +A Y +A Z diff --git a/racket/aoc2022/day-03/day-03.ipynb b/racket/aoc2022/day-03/day-03.ipynb new file mode 100644 index 0000000..27b8086 --- /dev/null +++ b/racket/aoc2022/day-03/day-03.ipynb @@ -0,0 +1,168 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Advent of Code 2022\n", + "#### Day 3: Rucksack Reorganization\n", + "\n", + "**Part 1.** Every elf has a rucksack with two compartments. What's the total priority value of the items that appear in both compartments of each sack?\n", + "\n", + "**Part 2.** Each group of three elves is carrying exactly one item in common. What's the total priority value of those common items?\n", + "\n", + "To ease the conversion between strings, lists and sets I bring in some utility functions from `relation`. " + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "vscode": { + "languageId": "racket" + } + }, + "outputs": [], + "source": [ + "(require racket\n", + " advent-of-code\n", + " threading\n", + " (only-in relation ->list ->set ->char))\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The items in each elf's inventory are represented by a string of alphabetic (case-sensitive) characters, each with a \"priority value\":\n", + "* Lowercase item types `a` through `z` have priorities 1 through 26.\n", + "* Uppercase item types `A` through `Z` have priorities 27 through 52." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "vscode": { + "languageId": "racket" + } + }, + "outputs": [], + "source": [ + "(define priority\n", + " (for/hash ([i (in-naturals 1)]\n", + " [c (in-sequences (inclusive-range 97 122) (inclusive-range 65 90))])\n", + " (values (->char c) i)))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### Part 1\n", + "\n", + "The front half and back half of the rucksack have one item in common we need to identify. For each rucksack in the inventory, we apply the following steps:\n", + "\n", + "1. Split the bag into the two halves,\n", + "2. find the common item in both halves (the intersection of the sets),\n", + "3. then look up that common item's value. " + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "vscode": { + "languageId": "racket" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "7845" + ], + "text/plain": [ + "7845" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(define raw-inventory (~> (fetch-aoc-input (find-session) 2022 3) string-split))\n", + "\n", + "(for/sum ([bag (in-list raw-inventory)])\n", + " (define len (/ (string-length bag) 2))\n", + " (~>> bag\n", + " ->list\n", + " ((λ (xs) (list (take xs len) (drop xs len)))) ; step 1\n", + " (apply set-intersect _) ; step 2\n", + " set-first\n", + " (hash-ref priority))) ; step 3\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### Part 2\n", + "\n", + "Now we need to take three rucksacks at a time and find the common item they all share. The procedure is simpler than Part 1's, especially with the `in-slice` function that automatically forms groups of three." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "vscode": { + "languageId": "racket" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "2790" + ], + "text/plain": [ + "2790" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(for/sum ([bags (in-slice 3 raw-inventory)])\n", + " (~>> bags (map ->set) (apply set-intersect) set-first (hash-ref priority)))\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Racket (Trusted)", + "language": "racket", + "name": "racket-trusted" + }, + "language_info": { + "codemirror_mode": "scheme", + "file_extension": ".rkt", + "mimetype": "text/x-racket", + "name": "Racket", + "pygments_lexer": "racket", + "version": "8.7" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "7454d72389401259f8ab87ac90deac92d19baedf4a52e60301852b1f4c653c5c" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/racket/aoc2022/day-04/day-04.ipynb b/racket/aoc2022/day-04/day-04.ipynb new file mode 100644 index 0000000..44c8980 --- /dev/null +++ b/racket/aoc2022/day-04/day-04.ipynb @@ -0,0 +1,148 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Advent of Code 2022\n", + "#### Day 4: Camp Cleanup\n", + "\n", + "Each elf in a pair of elves is assigned a range of ID numbers.\n", + "\n", + "**Part 1.** How many pairs have one elf assigned to a range completely contained by the other's?\n", + "\n", + "**Part 2.** How many pairs have overlapping assignments?\n", + "\n", + "Since this problem heavily uses ranges, I'm using `rebellion/base/range` to do the heavy lifting." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "vscode": { + "languageId": "racket" + } + }, + "outputs": [], + "source": [ + "#lang iracket/lang #:require rackjure\n", + "(require advent-of-code\n", + " relation\n", + " rebellion/base/range)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### Part 1\n", + "\n", + "All the assignments look like `\"11-22,33-44\"`, so we write a function to extract the numbers from the string with a regex, decompose the values with structural matching, and construct a pair of [`rebellion` ranges](https://docs.racket-lang.org/rebellion/Ranges.html).\n", + "\n", + "Once we have the two ranges, we can use a predicate that tests if one completely contains the other or vice versa to count the corresponding assignments." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "vscode": { + "languageId": "racket" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "550" + ], + "text/plain": [ + "550" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(define assignments (~> (fetch-aoc-input (find-session) 2022 4) string-split))\n", + "\n", + "(define (parse-range str)\n", + " (match str\n", + " [(regexp #px\"(\\\\d+)-(\\\\d+),(\\\\d+)-(\\\\d+)\" (list _ a b c d))\n", + " (values (closed-range (->number a) (->number b)) (closed-range (->number c) (->number d)))]))\n", + "\n", + "(define (one-encloses-the-other? str)\n", + " (define-values (range1 range2) (parse-range str))\n", + " (or (range-encloses? range1 range2) (range-encloses? range2 range1)))\n", + "\n", + "(count one-encloses-the-other? assignments)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### Part 2\n", + "\n", + "The procedure for part 2 is the same, just using the predicate for overlapping ranges instead." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "vscode": { + "languageId": "racket" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "931" + ], + "text/plain": [ + "931" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(define (one-overlaps-the-other? str)\n", + " (define-values (range1 range2) (parse-range str))\n", + " (range-overlaps? range1 range2))\n", + "\n", + "(count one-overlaps-the-other? assignments)\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Racket (Trusted)", + "language": "racket", + "name": "racket-trusted" + }, + "language_info": { + "codemirror_mode": "scheme", + "file_extension": ".rkt", + "mimetype": "text/x-racket", + "name": "Racket", + "pygments_lexer": "racket", + "version": "8.7" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "916dbcbb3f70747c44a77c7bcd40155683ae19c65e1c03b4aa3499c5328201f1" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/racket/aoc2022/day-05/day-05.ipynb b/racket/aoc2022/day-05/day-05.ipynb new file mode 100644 index 0000000..34cf4e4 --- /dev/null +++ b/racket/aoc2022/day-05/day-05.ipynb @@ -0,0 +1,187 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Advent of Code 2022\n", + "#### Day 5: Supply Stacks\n", + "\n", + "You're operating a crane, following a set of instructions telling you how many boxes to move from one stack to another. After you follow the instructions, the top boxes in the stacks spell out a message.\n", + "\n", + "**Part 1.** What's the message if the crane moves one box at a time?\n", + "\n", + "**Part 2.** What's the message if the crane moves all of the boxes at once?\n", + "\n", + "I'm bringing in my usual utility functions." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "(require racket\n", + " advent-of-code\n", + " threading\n", + " (only-in relation ->string ->list ->number)\n", + " (only-in algorithms chunks-of))\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The data file in this one is unusual: the first 10 lines are a pictorial representation of the stacks of boxes, and the remaining lines are instructions in the form `move X from Y to Z`.\n", + "\n", + "Tackling the boxes first, I can get the contents of each stack if I treat the picture as an array and transpose it, drop the first row that contains only brackets, then only take rows 1, 5, 9..., which are the rows with letters in them and trim the leading spaces from each row. I then use this list to create a hashtable." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "(define assignments (~> (fetch-aoc-input (find-session) 2022 5) (string-split \"\\n\")))\n", + "\n", + "(define crates-list\n", + " (~>> assignments\n", + " (take _ 8)\n", + " (map ->list)\n", + " (apply map list _)\n", + " rest\n", + " (chunks-of _ 4)\n", + " (map (λ~> first ->string string-trim ->list) _)))\n", + "\n", + "(define crates\n", + " (for/hash ([c (in-list crates-list)] [i (in-naturals 1)])\n", + " (values i c)))\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The instructions are a little easier; this is just a regex and destructuring like in Day 4." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "(struct instruction (n from to))\n", + "\n", + "(define (parse-instruction str)\n", + " (match str\n", + " [(regexp #px\"move (\\\\d+) from (\\\\d) to (\\\\d)\" (list _ n from to))\n", + " (instruction (->number n) (->number from) (->number to))]))\n", + "\n", + "(define instructions (~>> assignments (drop _ 10) (map parse-instruction)))\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### Part 1\n", + "\n", + "The main function to iterate over the list of instructions is the same for both parts, except for whether the boxes taken off of the origin stack are reversed or not when they end up on the destination stack. They end up reversed if they're taken off one at a time, and don't reverse if the whole stack is picked up at once.\n", + "\n", + "Once I've iterated through all the instructions, the `#:result` clause parses the final crate state." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\"WHTLRMZRC\"" + ], + "text/plain": [ + "\"WHTLRMZRC\"" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(define (find-crate-message cs [reverse? #true])\n", + " (define direction (if reverse? reverse identity))\n", + " (for/fold ([current-crates cs]\n", + " #:result (~>> (hash-values current-crates) (map first) (apply string)))\n", + " ([i (in-list instructions)])\n", + " (match-define (instruction n from to) i)\n", + " (define taken (~> (hash-ref current-crates from) (take _ n) direction))\n", + " (~> current-crates\n", + " (hash-update _ from (λ (v) (drop v n)))\n", + " (hash-update _ to (λ (v) (append taken v))))))\n", + "\n", + "(find-crate-message crates)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### Part 2\n", + "\n", + "The result, if the moved boxes don't get flipped:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\"GMPMLWNMG\"" + ], + "text/plain": [ + "\"GMPMLWNMG\"" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(find-crate-message crates #false)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Racket (Trusted)", + "language": "racket", + "name": "racket-trusted" + }, + "language_info": { + "codemirror_mode": "scheme", + "file_extension": ".rkt", + "mimetype": "text/x-racket", + "name": "racket", + "pygments_lexer": "racket", + "version": "8.7" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "916dbcbb3f70747c44a77c7bcd40155683ae19c65e1c03b4aa3499c5328201f1" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/racket/aoc2022/day-05/day-05.rkt b/racket/aoc2022/day-05/day-05.rkt new file mode 100644 index 0000000..76d4ca6 --- /dev/null +++ b/racket/aoc2022/day-05/day-05.rkt @@ -0,0 +1,46 @@ +#lang racket + +(require advent-of-code + threading + (only-in relation ->string ->list ->number) + (only-in algorithms chunks-of)) + +(define data (~> (fetch-aoc-input (find-session) 2022 5) (string-split "\n"))) + +(struct instruction (n from to)) + +(define crates-list + (~>> data + (take _ 8) + (map (λ~>> ->list)) + (apply map list _) + rest + (chunks-of _ 4) + (map (λ~> first ->string string-trim ->list) _))) + +(define crates + (for/hash ([c (in-list crates-list)] [i (in-naturals 1)]) + (values i c))) + +(define (parse-instruction str) + (match str + [(regexp #px"move (\\d+) from (\\d) to (\\d)" (list _ n from to)) + (instruction (->number n) (->number from) (->number to))])) + +(define instructions (~>> data (drop _ 10) (map parse-instruction))) + +(define (find-crate-message cs [reverse-function reverse]) + (for/fold ([current-crates cs] + #:result (~>> (hash-values current-crates) (map first) (apply string))) + ([i (in-list instructions)]) + (match-define (instruction n from to) i) + (define taken (~> (hash-ref current-crates from) (take _ n) reverse-function)) + (~> current-crates + (hash-update _ from (λ (v) (drop v n))) + (hash-update _ to (λ (v) (append taken v)))))) + +;; part 1 +(find-crate-message crates) + +;; part 2 +(find-crate-message crates identity) diff --git a/racket/aoc2022/day-06/day-06.ipynb b/racket/aoc2022/day-06/day-06.ipynb new file mode 100644 index 0000000..0c89fa1 --- /dev/null +++ b/racket/aoc2022/day-06/day-06.ipynb @@ -0,0 +1,122 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Advent of Code 2022\n", + "#### Day 6: Tuning Trouble\n", + "\n", + "You're parsing a buffer of characters, looking for a \"marker\": the index of the first character where the previous $n$ characters are all unique.\n", + "\n", + "**Part 1.** What if $n = 4$, for the \"start of packet\" marker?\n", + "\n", + "**Part 2.** What if $n = 14$, for the \"start of message\" marker?" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "(require racket\n", + " advent-of-code\n", + " (only-in algorithms sliding))\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### Parts 1 and 2\n", + "\n", + "`(sliding xs n)` returns all the overlapping sublists of `xs` that are `n` elements wide. This turns this problem into a simple `for` comprehension, just looking for the first sublist with no duplicates (which means its length after deduplication doesn't change).\n", + "\n", + "The solution to part 1 and part 2 is the same, just differing in how wide the window is." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "1042" + ], + "text/plain": [ + "1042" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(define buffer (fetch-aoc-input (find-session) 2022 6))\n", + "\n", + "(define (find-marker data type)\n", + " (define n\n", + " (match type\n", + " ['start-of-packet 4]\n", + " ['start-of-message 14]))\n", + " (for/first ([chunk (in-list (sliding (string->list data) n))]\n", + " [i (in-naturals n)]\n", + " #:when (= n (~> chunk remove-duplicates length)))\n", + " i))\n", + "\n", + "(find-marker buffer 'start-of-packet)\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "2980" + ], + "text/plain": [ + "2980" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(find-marker buffer 'start-of-message)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Racket (trusted)", + "language": "racket", + "name": "racket-trusted" + }, + "language_info": { + "codemirror_mode": "scheme", + "file_extension": ".rkt", + "mimetype": "text/x-racket", + "name": "racket", + "pygments_lexer": "racket", + "version": "8.7" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "916dbcbb3f70747c44a77c7bcd40155683ae19c65e1c03b4aa3499c5328201f1" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/racket/aoc2022/day-06/day-06.rkt b/racket/aoc2022/day-06/day-06.rkt new file mode 100644 index 0000000..1c167a6 --- /dev/null +++ b/racket/aoc2022/day-06/day-06.rkt @@ -0,0 +1,22 @@ +#lang racket + +(require advent-of-code + (only-in algorithms sliding)) + +(define buffer (fetch-aoc-input (find-session) 2022 6)) + +(define (find-marker data type) + (define n + (case type + [(start-of-packet) 4] + [(start-of-message) 14])) + (for/first ([chunk (in-list (sliding (string->list data) n))] + [i (in-naturals n)] + #:unless (check-duplicates chunk)) + i)) + +;; part 1 +(find-marker buffer 'start-of-packet) + +;; part 2 +(find-marker buffer 'start-of-message) diff --git a/racket/aoc2022/day-07/day-07.rkt b/racket/aoc2022/day-07/day-07.rkt new file mode 100644 index 0000000..3826cc4 --- /dev/null +++ b/racket/aoc2022/day-07/day-07.rkt @@ -0,0 +1,36 @@ +#lang racket + +(require advent-of-code + fancy-app + threading + (only-in relation ->number)) + +(define command-output (~> (fetch-aoc-input (find-session) 2022 7) (string-split "\n"))) + +(define (parse-commands cmds) + (define (update-sizes h path size) + (match path + ['() h] + [(list* _ fs) (update-sizes (hash-update h path (+ _ size) 0) fs size)])) + + (for/fold ([folders (hash)] [current-path '()] [previously-seen? #false] #:result folders) + ([cmd (in-list cmds)]) + (match (string-split cmd) + [(list "$" "ls") (values folders current-path (hash-has-key? folders current-path))] + [(list "$" "cd" "/") (values folders '("/") #false)] + [(list "$" "cd" "..") (values folders (rest current-path) #false)] + [(list "$" "cd" folder) (values folders (cons folder current-path) #false)] + [(list "dir" _) (values folders current-path previously-seen?)] + [(list (app ->number size) _) + (cond + [previously-seen? (values folders current-path previously-seen?)] + [else (values (update-sizes folders current-path size) current-path previously-seen?)])]))) + +(define folders (parse-commands command-output)) + +;; part 1 +(for/sum ([(_ v) (in-hash folders)] #:when (<= v 100000)) v) + +;; part 2 +(define required-to-delete (- 30000000 (- 70000000 (hash-ref folders '("/"))))) +(~>> folders hash-values (filter (> _ required-to-delete)) (apply min)) diff --git a/racket/aoc2022/day-08/day-08.ipynb b/racket/aoc2022/day-08/day-08.ipynb new file mode 100644 index 0000000..890a9bb --- /dev/null +++ b/racket/aoc2022/day-08/day-08.ipynb @@ -0,0 +1,257 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Advent of Code 2022\n", + "#### Day 8: Treetop Tree House\n", + "\n", + "For a rectangular grid of trees of varying heights,\n", + "\n", + "**Part 1.** How many trees are visible (not blocked by same-height or taller trees in their row or column) from outside the patch?\n", + "\n", + "**Part 2.** What's the tree with the best \"scenic score\" (the product of the number of trees it can see in each cardinal direction)?\n", + "\n", + "For this solution, I didn't use any packages besides the standard distribution and `advent-of-code`." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "vscode": { + "languageId": "racket" + } + }, + "outputs": [], + "source": [ + "(require racket\n", + " advent-of-code)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### Part 1\n", + "\n", + "I built this array as a hashtable with a coordinate struct as the key and the tree height as the value, which makes it easy to check if a particular location falls outside the grid." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "vscode": { + "languageId": "racket" + } + }, + "outputs": [], + "source": [ + "(struct posn (r c) #:transparent)\n", + "\n", + "(define (make-tree-grid data)\n", + " (for*/hash ([(row r) (in-indexed data)] [(col c) (in-indexed (in-string row))])\n", + " (values (posn r c) (string->number (string col)))))\n", + "\n", + "(define tree-grid (make-tree-grid (in-lines (open-aoc-input (find-session) 2022 8))))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A tree is visible if there aren't any other trees as tall as it or taller blocking the visibility in at least one of the cardinal directions. We check in a particular direction by repeatedly stepping outwards from the original coordinate and checking if we've found a blocking tree yet.\n", + "\n", + "The `for/first` returns `#true` if it finds a blocking tree and `#false` if it runs out of trees to check, and the result is negated to turn it into a predicate about whether the tree can see (and be seen from) outside." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "vscode": { + "languageId": "racket" + } + }, + "outputs": [], + "source": [ + "(define (can-see-to-outside? p height dr dc h)\n", + " (match-define (posn r c) p)\n", + " (not (for/first ([n (in-naturals 1)]\n", + " #:do [(define p* (posn (+ r (* n dr)) (+ c (* n dc))))]\n", + " #:break (not (hash-has-key? h p*))\n", + " #:when (<= height (hash-ref h p*)))\n", + " #true)))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we need a function to check the four cardinal directions and see if at least one of them is unblocked." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "vscode": { + "languageId": "racket" + } + }, + "outputs": [], + "source": [ + "(define (visible-in-any-direction? p height h)\n", + " (for*/or ([dr (in-list '(-1 0 1))] [dc (in-list '(-1 0 1))] #:when (= 1 (abs (+ dr dc))))\n", + " (can-see-to-outside? p height dr dc h)))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Finally, add 1 to the count for every tree that's visible:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "vscode": { + "languageId": "racket" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "1733" + ], + "text/plain": [ + "1733" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(define (count-visible-trees trees)\n", + " (for/sum ([(tree-posn height) (in-hash trees)])\n", + " (cond\n", + " [(visible-in-any-direction? tree-posn height trees) 1]\n", + " [else 0])))\n", + "\n", + "(count-visible-trees tree-grid)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### Part 2\n", + "\n", + "Now we're searching for the tree with the most interior visibility, which is the product of how far we can look in each cardinal direction before running out of visible trees. \n", + "\n", + "The search is similar, except now we're calculating a score instead of just checking a predicate. We walk one step at a time, increment the counter and check each tree; if we find a blocking tree, we return the counter, and if we run out of trees to check we break and return the previous counter value. If there aren't any trees in a direction and the `for` body is never evaluated, `for/last` returns `#false`, which is equivalent to a visibility of 0." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "vscode": { + "languageId": "racket" + } + }, + "outputs": [], + "source": [ + "(define (scenic-score-in-direction p height dr dc h)\n", + " (match-define (posn r c) p)\n", + " (define score\n", + " (for/last ([n (in-naturals 1)]\n", + " #:do [(define p* (posn (+ r (* n dr)) (+ c (* n dc))))]\n", + " #:break (not (hash-has-key? h p*))\n", + " #:final (<= height (hash-ref h p*)))\n", + " n))\n", + " (if (not score) 0 score))\n", + "\n", + "(define (scenic-score p height h)\n", + " (for*/product ([dr (in-list '(-1 0 1))] [dc (in-list '(-1 0 1))] #:when (= 1 (abs (+ dr dc))))\n", + " (scenic-score-in-direction p height dr dc h)))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "With these functions written, calculate each tree's score and find the maximum:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "vscode": { + "languageId": "racket" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "284648" + ], + "text/plain": [ + "284648" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(define (find-best-scenic-score trees)\n", + " (apply max\n", + " (for/list ([(tree-posn height) (in-hash trees)])\n", + " (scenic-score tree-posn height trees))))\n", + "\n", + "(find-best-scenic-score tree-grid)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Racket (Trusted)", + "language": "racket", + "name": "racket-trusted" + }, + "language_info": { + "codemirror_mode": "scheme", + "file_extension": ".rkt", + "mimetype": "text/x-racket", + "name": "Racket", + "pygments_lexer": "racket", + "version": "8.7" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "916dbcbb3f70747c44a77c7bcd40155683ae19c65e1c03b4aa3499c5328201f1" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/racket/aoc2022/day-08/day-08.rkt b/racket/aoc2022/day-08/day-08.rkt new file mode 100644 index 0000000..6b60eca --- /dev/null +++ b/racket/aoc2022/day-08/day-08.rkt @@ -0,0 +1,56 @@ +#lang racket + +(require advent-of-code) + +(struct posn (r c) #:transparent) + +(define (make-tree-grid data) + (for*/hash ([(row r) (in-indexed data)] [(col c) (in-indexed (in-string row))]) + (values (posn r c) (string->number (string col))))) + +(define tree-grid (make-tree-grid (in-lines (open-aoc-input (find-session) 2022 8)))) + +;; part 1 + +(define (can-see-to-outside? p height dr dc h) + (match-define (posn r c) p) + (not (for/first ([n (in-naturals 1)] + #:do [(define p* (posn (+ r (* n dr)) (+ c (* n dc))))] + #:break (not (hash-has-key? h p*)) + #:when (<= height (hash-ref h p*))) + #true))) + +(define (visible-in-any-direction? p height h) + (for*/or ([dr (in-list '(-1 0 1))] [dc (in-list '(-1 0 1))] #:when (= 1 (abs (+ dr dc)))) + (can-see-to-outside? p height dr dc h))) + +(define (count-visible-trees trees) + (for/sum ([(tree-posn height) (in-hash trees)]) + (cond + [(visible-in-any-direction? tree-posn height trees) 1] + [else 0]))) + +(count-visible-trees tree-grid) + +;; part 2 + +(define (scenic-score-in-direction p height dr dc h) + (match-define (posn r c) p) + (define score + (for/last ([n (in-naturals 1)] + #:do [(define p* (posn (+ r (* n dr)) (+ c (* n dc))))] + #:break (not (hash-has-key? h p*)) + #:final (<= height (hash-ref h p*))) + n)) + (if (not score) 0 score)) + +(define (scenic-score p height h) + (for*/product ([dr (in-list '(-1 0 1))] [dc (in-list '(-1 0 1))] #:when (= 1 (abs (+ dr dc)))) + (scenic-score-in-direction p height dr dc h))) + +(define (find-best-scenic-score trees) + (apply max + (for/list ([(tree-posn height) (in-hash trees)]) + (scenic-score tree-posn height trees)))) + +(find-best-scenic-score tree-grid) diff --git a/racket/aoc2022/day-09/day-09.rkt b/racket/aoc2022/day-09/day-09.rkt new file mode 100644 index 0000000..0390d2e --- /dev/null +++ b/racket/aoc2022/day-09/day-09.rkt @@ -0,0 +1,76 @@ +#lang racket +(require advent-of-code + threading) + +(struct cmd (dir amt)) +(struct posn (x y) #:transparent) + +(define moves + (~> (fetch-aoc-input (find-session) 2022 9) + (string-split "\n") + (map (λ~> (string-split _) + (match _ + [(list dir amt) (cmd (string->symbol dir) (string->number amt))])) + _))) + +(define (move-head p dir) + (match-define (posn x y) p) + (match dir + ['U (posn x (add1 y))] + ['D (posn x (sub1 y))] + ['R (posn (add1 x) y)] + ['L (posn (sub1 x) y)])) + +(define (avg n m) + (/ (+ n m) 2)) + +(define (manhattan-distance p1 p2) + (match-define (posn x1 y1) p1) + (match-define (posn x2 y2) p2) + (+ (abs (- x2 x1)) (abs (- y2 y1)))) + +(define (follow-head head tail) + (match-define (posn hx hy) head) + (match-define (posn tx ty) tail) + + (case (manhattan-distance head tail) + [(0 1) tail] + [(2 4) + (cond + [(and (= 1 (abs (- hx tx)) (abs (- hy ty)))) tail] + [else (posn (avg hx tx) (avg hy ty))])] + [(3) + (cond + [(= 2 (abs (- hx tx))) (posn (avg hx tx) hy)] + [(= 2 (abs (- hy ty))) (posn hx (avg hy ty))])])) + +;; part 1 +(for*/fold ([head (posn 0 0)] [tail (posn 0 0)] [tail-posns (set)] #:result (set-count tail-posns)) + ([move (in-list moves)] #:do [(match-define (cmd dir amt) move)] [_ (in-range amt)]) + (define new-head (move-head head dir)) + (define new-tail (follow-head new-head tail)) + (values new-head new-tail (set-add tail-posns new-tail))) + +;; part 2 +(for*/fold ([knots (make-list 10 (posn 0 0))] [tail-posns (set)] #:result (set-count tail-posns)) + ([move (in-list moves)] #:do [(match-define (cmd dir amt) move)] [_ (in-range amt)]) + (define updated-knots + (for/fold ([knots-list (list (move-head (first knots) dir))]) + ([following-knot (in-list (rest knots))]) + (cons (follow-head (car knots-list) following-knot) knots-list))) + (values (reverse updated-knots) (set-add tail-posns (first updated-knots)))) + +;; refactor: part 1 and 2 combined +(define (follow-tail move-list rope-length) + (for*/fold ([knots (make-list rope-length (posn 0 0))] + [tail-posns (set)] + #:result (set-count tail-posns)) + ([move (in-list move-list)] #:do [(match-define (cmd dir amt) move)] [_ (in-range amt)]) + (define updated-knots + (for/fold ([knots-list (list (move-head (first knots) dir))]) + ([following-knot (in-list (rest knots))]) + (cons (follow-head (car knots-list) following-knot) knots-list))) + (values (reverse updated-knots) (set-add tail-posns (first updated-knots))))) + +(time (follow-tail moves 2)) +(time (follow-tail moves 10)) diff --git a/racket/aoc2022/day-10/day-10.rkt b/racket/aoc2022/day-10/day-10.rkt new file mode 100644 index 0000000..70c80d3 --- /dev/null +++ b/racket/aoc2022/day-10/day-10.rkt @@ -0,0 +1,43 @@ +#lang racket + +(require advent-of-code + threading + fancy-app + (only-in algorithms chunks-of)) + +(define/match (process-instruction _) + [((list "noop")) (list 'noop)] + [((list "addx" (app string->number val))) (list 'noop (cons 'addx val))]) + +(define instructions + (~> (fetch-aoc-input (find-session) 2022 10) + (string-split "\n") + (map (λ~> string-split process-instruction) _) + (apply append _))) + +;; part 1 +(define interesting-times (inclusive-range 20 220 40)) + +(define/match (evaluate-instruction _op acc) + [('noop _) acc] + [((cons 'addx n) _) (+ acc n)]) + +(for/fold ([acc 1] [interesting-strengths 0] #:result interesting-strengths) + ([inst (in-list instructions)] [i (in-naturals 1)]) + (define new-interesting + (if (member i interesting-times) (+ interesting-strengths (* i acc)) interesting-strengths)) + (values (evaluate-instruction inst acc) new-interesting)) + +;; part 2 +(for/fold ([acc 1] [pixels '()] #:result (~> pixels reverse (chunks-of 40) (map (apply string _) _))) + ([inst (in-list instructions)] [i (in-naturals)]) + (define new-pixel (if (member (modulo i 40) (list (sub1 acc) acc (add1 acc))) #\█ #\space)) + (values (evaluate-instruction inst acc) (cons new-pixel pixels))) + +; for my data set, +; '("███ ████ ████ █ █ ████ ████ █ █ ██ " +; "█ █ █ █ █ █ █ █ █ █ █ █ " +; "█ █ █ ███ ██ ███ ███ ████ █ █ " +; "███ █ █ █ █ █ █ █ █ ████ " +; "█ █ █ █ █ █ █ █ █ █ █ █ " +; "█ █ ████ ████ █ █ ████ █ █ █ █ █ ") diff --git a/racket/aoc2022/day-11/day-11.rkt b/racket/aoc2022/day-11/day-11.rkt new file mode 100644 index 0000000..af7b4ee --- /dev/null +++ b/racket/aoc2022/day-11/day-11.rkt @@ -0,0 +1,85 @@ +#lang racket +(require threading) + +(struct monkey ([items #:mutable] operation modulus yes no)) + +;; really don't feel like parsing the input today +(define monkeys-list + (list (cons 0 (monkey '(97 81 57 57 91 61) + (λ (n) (* n 7)) + 11 5 6)) + (cons 1 (monkey '(88 62 68 90) + (λ (n) (* n 17)) + 19 4 2)) + (cons 2 (monkey '(74 87) + (λ (n) (+ n 2)) + 5 7 4)) + (cons 3 (monkey '(53 81 60 87 90 99 75) + (λ (n) (+ n 1)) + 2 2 1)) + (cons 4 (monkey '(57) + (λ (n) (+ n 6)) + 13 7 0)) + (cons 5 (monkey '(54 84 91 55 59 72 75 70) + (λ (n) (* n n)) + 7 6 3)) + (cons 6 (monkey '(95 79 79 68 78) + (λ (n) (+ n 3)) + 3 1 3)) + (cons 7 (monkey '(61 97 67) + (λ (n) (+ n 4)) + 17 0 5)))) + +(define monkey-lcm (~> monkeys-list (map (λ~> cdr monkey-modulus) _) (apply lcm _))) + +;; part 1 +(define monkeys-count-pt1 (make-hash)) +(define monkeys-hash-pt1 (make-hash monkeys-list)) + +(for + ([rnd (in-range 20)]) + (for + ([turn (inclusive-range 0 7)]) + (match-define (monkey items op modulus yes no) (hash-ref monkeys-hash-pt1 turn)) + (for ([i (in-list items)]) + (define i* (quotient (op i) 3)) + (define m (if (= 0 (modulo i* modulus)) yes no)) + (match-define (monkey items* op* modulus* yes* no*) (hash-ref monkeys-hash-pt1 m)) + (hash-update! monkeys-count-pt1 turn add1 0) + (hash-set! monkeys-hash-pt1 + m + (monkey (append items* (list i*)) op* modulus* yes* no*))) + (hash-set! monkeys-hash-pt1 turn (monkey '() op modulus yes no)))) + +(~> monkeys-count-pt1 + hash->list + (sort _ > #:key cdr) + (take _ 2) + (map cdr _) + (apply * _)) + +;; part 2 +(define monkeys-count-pt2 (make-hash)) +(define monkeys-hash-pt2 (make-hash monkeys-list)) + +(for + ([rnd (in-range 10000)]) + (for + ([turn (inclusive-range 0 7)]) + (match-define (monkey items op modulus yes no) (hash-ref monkeys-hash-pt2 turn)) + (for ([i (in-list items)]) + (define i* (op i)) + (define m (if (= 0 (modulo i* modulus)) yes no)) + (match-define (monkey items* op* modulus* yes* no*) (hash-ref monkeys-hash-pt2 m)) + (hash-update! monkeys-count-pt2 turn add1 0) + (hash-set! monkeys-hash-pt2 + m + (monkey (append items* (list (remainder i* monkey-lcm))) op* modulus* yes* no*))) + (hash-set! monkeys-hash-pt2 turn (monkey '() op modulus yes no)))) + +(~> monkeys-count-pt2 + hash->list + (sort _ > #:key cdr) + (take _ 2) + (map cdr _) + (apply * _)) \ No newline at end of file diff --git a/racket/aoc2022/day-12/day-12.rkt b/racket/aoc2022/day-12/day-12.rkt new file mode 100644 index 0000000..c3f01ac --- /dev/null +++ b/racket/aoc2022/day-12/day-12.rkt @@ -0,0 +1,46 @@ +#lang racket + +(require advent-of-code + graph) + +(define raw-terrain (fetch-aoc-input (find-session) 2022 12 #:cache #true)) +(define special-points (make-hash)) + +(define terrain-mesh + (for*/hash ([(row x) (in-indexed (string-split raw-terrain))] [(col y) (in-indexed row)]) + (define p (cons x y)) + (case col + [(#\S) + (hash-set! special-points 'start p) + (values p 0)] + [(#\E) + (hash-set! special-points 'end p) + (values p 25)] + [else (values p (- (char->integer col) (char->integer #\a)))]))) + +(define (neighbors p) + (match-define (cons x y) p) + (for*/list ([dx (in-list '(-1 0 1))] + [dy (in-list '(-1 0 1))] + #:when (= 1 (abs (+ dx dy))) + #:do [(define p* (cons (+ x dx) (+ y dy)))] + #:when (hash-has-key? terrain-mesh p*)) + p*)) + +(define paths + (directed-graph (for*/list ([p (in-list (hash-keys terrain-mesh))] + [p* (in-list (neighbors p))] + #:unless (> (sub1 (hash-ref terrain-mesh p*)) + (hash-ref terrain-mesh p))) + (list p p*)))) + +;; part 1 +(time (match-define-values (distances _) (bfs paths (hash-ref special-points 'start))) + (hash-ref distances (hash-ref special-points 'end))) + +;; part 2 +(time (for/lists + (lengths #:result (apply min lengths)) + ([start (in-list (hash-keys terrain-mesh))] #:when (= 0 (hash-ref terrain-mesh start))) + (match-define-values (distances _) (bfs paths start)) + (hash-ref distances (hash-ref special-points 'end)))) diff --git a/racket/aoc2022/day-13/day-13.rkt b/racket/aoc2022/day-13/day-13.rkt new file mode 100644 index 0000000..39435e9 --- /dev/null +++ b/racket/aoc2022/day-13/day-13.rkt @@ -0,0 +1,28 @@ +#lang racket + +(require advent-of-code) + +(define raw-packets + (parameterize ([current-readtable (make-readtable #f #\, #\space #f)]) + (port->list read (open-aoc-input (find-session) 2022 13 #:cache #true)))) + +(define (compare xs ys) + (match* (xs ys) + [('() (list* _)) #true] + [((list* _) '()) #false] + [((list* _same x-rest) (list* _same y-rest)) (compare x-rest y-rest)] + [((list* (? integer? x) _) (list* (? integer? y) _)) (< x y)] + [((list* (? list? xs*) _) (list* (? list? ys*) _)) (compare xs* ys*)] + [(xs (list* (? integer? y) y-rest)) (compare xs (cons (list y) y-rest))] + [((list* (? integer? x) x-rest) ys) (compare (cons (list x) x-rest) ys)])) + +;; part 1 +(for/sum ([i (in-naturals 1)] [packet (in-slice 2 raw-packets)] #:when (apply compare packet)) i) + +;; part 2 +(define divider-packets (list '((2)) '((6)))) +(define amended-packets (append divider-packets raw-packets)) + +(for/product ([i (in-naturals 1)] [packet (in-list (sort amended-packets compare))] + #:when (member packet divider-packets)) + i) diff --git a/racket/aoc2022/day-14/day-14.rkt b/racket/aoc2022/day-14/day-14.rkt new file mode 100644 index 0000000..88ba297 --- /dev/null +++ b/racket/aoc2022/day-14/day-14.rkt @@ -0,0 +1,51 @@ +#lang racket + +(require advent-of-code + threading + algorithms) + +(define data (fetch-aoc-input (find-session) 2022 14 #:cache #true)) + +(define (trace-line-between-points p1 p2) + (match* (p1 p2) + [((list x y1) (list x y2)) (map (λ (y) (cons x y)) (inclusive-range (min y1 y2) (max y1 y2)))] + [((list x1 y) (list x2 y)) (map (λ (x) (cons x y)) (inclusive-range (min x1 x2) (max x1 x2)))])) + +(define (find-points-in-structure str) + (define endpoints + (for/list ([coord-pair (in-list (string-split str " -> "))]) + (for/list ([coord (in-list (string-split coord-pair ","))]) + (string->number coord)))) + (~>> endpoints (adjacent-map trace-line-between-points) (apply append) (list->set))) + +(define blocked-locations + (~> data (string-split "\n") (map find-points-in-structure _) (apply set-union _))) + +(define max-vertical-distance (~>> blocked-locations (set->list) (argmax cdr) cdr add1)) + +(define (open? pts p) + (not (set-member? pts p))) + +;; part 1 +(define (trace-grain pts path #:at-limit do-at-limit) + (match-define (list* (and p (cons x y)) _) path) + (match-define (list dest-1 dest-2 dest-3) (map (λ (d) (cons (+ x d) (add1 y))) '(0 -1 1))) + (cond + [(>= y max-vertical-distance) (values (do-at-limit pts p) path)] + [(open? pts dest-1) (trace-grain pts (cons dest-1 path) #:at-limit do-at-limit)] + [(open? pts dest-2) (trace-grain pts (cons dest-2 path) #:at-limit do-at-limit)] + [(open? pts dest-3) (trace-grain pts (cons dest-3 path) #:at-limit do-at-limit)] + [else (values (set-add pts (car path)) path)])) + +(time (for/fold ([pts blocked-locations] [path (list (cons 500 0))] [grains 0] #:result grains) + ([_ (in-naturals 1)]) + (define-values (pts* path*) (trace-grain pts path #:at-limit (const 'break))) + #:break (equal? pts* 'break) + (values pts* (cdr path*) (add1 grains)))) + +;; part 2 +(time (for/fold ([pts blocked-locations] [path (list (cons 500 0))] [grains 0] #:result grains) + ([_ (in-naturals 1)]) + #:break (not (open? pts (cons 500 0))) + (define-values (pts* path*) (trace-grain pts path #:at-limit set-add)) + (values pts* (cdr path*) (add1 grains)))) diff --git a/racket/aoc2022/day-15/day-15.rkt b/racket/aoc2022/day-15/day-15.rkt new file mode 100644 index 0000000..b050807 --- /dev/null +++ b/racket/aoc2022/day-15/day-15.rkt @@ -0,0 +1,54 @@ +#lang racket + +(require advent-of-code + threading + fancy-app + algorithms + (prefix-in iset- data/integer-set)) + +(struct beacon-record (sensor beacon)) +(struct posn (x y)) + +(define beacon-records + (~> (fetch-aoc-input (find-session) 2022 15 #:cache #true) + (string-split "\n") + (map (λ~> (string-replace #px"[^\\d\\s-]" "") + string-split + (map string->number _) + (chunks-of 2) + (map (apply posn _) _) + (apply beacon-record _)) + _))) + +(define (manhattan-distance-to-beacon record) + (match-define (beacon-record (posn sx sy) (posn bx by)) record) + (+ (abs (- sx bx)) (abs (- sy by)))) + +(define (coverage-at-row record row) + (match-define (beacon-record (posn sx sy) _) record) + (define x-distance (- (manhattan-distance-to-beacon record) (abs (- row sy)))) + (cond + [(negative? x-distance) (iset-make-range)] + [else (iset-make-range (- sx x-distance) (+ sx x-distance))])) + +(define (total-coverage-at-row records row) + (for/fold ([coverage (iset-make-range)]) ([r (in-list records)]) + (iset-union coverage (coverage-at-row r row)))) + +;; part 1 +(define (coverage-without-beacons records row) + (~> (total-coverage-at-row records row) + iset-count + (- (count (λ (b) (= row (posn-y b))) + (~> beacon-records (map beacon-record-beacon _) remove-duplicates))))) + +(coverage-without-beacons beacon-records 2000000) + +;; part 2 +(define (find-only-beacon beacon-records size) + (for/first ([y (in-range 0 size)] + #:do [(define xs (iset-complement (total-coverage-at-row beacon-records y) 0 size))] + #:when (= 1 (iset-count xs))) + (+ (* 4000000 (iset-get-integer xs)) y))) + +(find-only-beacon beacon-records 4000000) diff --git a/racket/aoc2022/day-16/day-16.rkt b/racket/aoc2022/day-16/day-16.rkt new file mode 100644 index 0000000..5ec56d6 --- /dev/null +++ b/racket/aoc2022/day-16/day-16.rkt @@ -0,0 +1,107 @@ +#lang racket + +(require advent-of-code + fancy-app + graph + threading) + +(define (process-line str) + (match str + [(regexp #px"Valve (\\w\\w) has flow rate=(\\d+); tunnels? leads? to valves? (.+)" + (list _ name (app string->number rate) (app (string-split _ ", ") valves))) + (list name rate valves)])) + +(define cave-data + (~> (fetch-aoc-input (find-session) 2022 16 #:cache #true) + (string-split "\n") + (map process-line _))) + +(define cave-map + (for*/lists (tunnels #:result (directed-graph tunnels)) + ([valve (in-list cave-data)] #:do [(match-define (list name _ valves) valve)] + [destination (in-list valves)]) + (list name destination))) + +(define valve-flows + (for/hash ([valve (in-list cave-data)] + #:do [(match-define (list name flow _) valve)] + #:when (> flow 0)) + (values name flow))) + +(define shortest-path-lengths (johnson cave-map)) + +(define (reachable-destinations start dests minutes-left) + (for/list ([(dest _) (in-hash dests)] + #:do [(define travel-time + (hash-ref shortest-path-lengths (list start dest) minutes-left))] + #:when (<= 1 travel-time minutes-left)) + (cons dest travel-time))) + +;; part 1 +(define (find-best-single-route start + [minutes-left 30] + [current-pressure 0] + [available-valves valve-flows]) + (cond + [(>= minutes-left 1) + (for/fold ([running-pressure current-pressure] + [remaining-valves available-valves] + #:result (cons running-pressure remaining-valves)) + ([candidate (reachable-destinations start available-valves minutes-left)]) + (match-define (cons dest travel-time) candidate) + (define minutes-left* (- minutes-left (add1 travel-time))) + (match-define (cons candidate-pressure remaining-valves*) + (find-best-single-route dest + minutes-left* + (+ current-pressure (* (hash-ref valve-flows dest) minutes-left*)) + (hash-remove available-valves dest))) + (if (> candidate-pressure running-pressure) + (values candidate-pressure remaining-valves*) + (values running-pressure remaining-valves*)))] + [else (cons current-pressure available-valves)])) + +(car (find-best-single-route "AA")) + +;; part 2 + +(define (possible-paths start dests minutes-left) + (cond + [(or (hash-empty? dests) (< minutes-left 3)) '()] + [else + (for/fold ([path '()]) ([dest (in-list (reachable-destinations start dests minutes-left))]) + (match-define (cons valve minutes) dest) + (define dests* (hash-remove dests valve)) + (define next-valves (possible-paths valve dests* (- minutes-left minutes))) + (append (list (list dest)) (map (cons dest _) next-valves) path))])) + +(define (flow-for-path path minutes [sum 0]) + (match path + ['() sum] + [(list* (cons valve dist) tail) + (define valve-open-for (- minutes dist 1)) + (flow-for-path tail valve-open-for (+ sum (* (hash-ref valve-flows valve) valve-open-for)))])) + +(define minutes-left 26) + +(define human-paths + (~>> (possible-paths "AA" valve-flows minutes-left) + (map (λ (path) (cons (flow-for-path path minutes-left) (map car path)))) + (sort _ > #:key car))) + +(define (best-possible-elephant-path human-path) + (define remaining-dests + (for/hash ([(dest flow) (in-hash valve-flows)] #:unless (member dest (cdr human-path))) + (values dest flow))) + (~>> (possible-paths "AA" remaining-dests minutes-left) + (map (λ (path) (cons (flow-for-path path minutes-left) (map car path)))) + (sort _ > #:key car) + car)) + +;; this takes a long time to run but I stuck a displayln in for debugging +;; and just took the highest max-flow after letting it run for a while and waiting until +;; it stopped printing new bests to console +(for*/fold ([max-flow 0]) + ([human-path (in-list human-paths)] + #:do [(define elephant-path (best-possible-elephant-path human-path))]) + (define combined-flow (+ (car human-path) (car elephant-path))) + (if (< max-flow combined-flow) combined-flow max-flow)) diff --git a/racket/aoc2022/day-17/day-17.rkt b/racket/aoc2022/day-17/day-17.rkt new file mode 100644 index 0000000..28e8763 --- /dev/null +++ b/racket/aoc2022/day-17/day-17.rkt @@ -0,0 +1,53 @@ +#lang racket + +(require advent-of-code + threading + fancy-app + data/collection) + +(define (relative-rock-coordinates rock) + (for*/hash ([(row y) (in-indexed (reverse rock))] [(_col x) (in-indexed row)]) + (values (cons x y) #true))) + +(define rock-shapes + (~> (open-input-file "./2022/day-17/rock-shapes") + port->string + (string-split "\n\n") + (map (λ~> (string-split _ "\n") (map string->list _) relative-rock-coordinates) _))) + +(define gusts + (~> (fetch-aoc-input (find-session) 2022 17 #:cache #true) + string-trim + string->list + (map (match-lambda + [#\> 1] + [#\< -1]) + _))) + +(define (place-new-rock rock elevation) + (for/hash ([(posn _) (in-hash rock)]) + (match-define (cons x y) posn) + (values (cons (+ x 3) (+ y elevation 4)) #true))) + +(define (gust-move rock wind cave) + (define moved-rock + (for/hash ([(posn _) (in-hash rock)]) + (match-define (cons x y) posn) + (define posn* (cons (+ x wind) y)) + (if (hash-has-key? cave posn) (values 'collision #t) (values posn* #t)))) + (if (hash-has-key? moved-rock 'collision) rock moved-rock)) + +(for/fold ([cave (hash)] + [falling-rock #f] + [top-of-rocks 0] + [rock-cycle (cycle rock-shapes)] + [rock-number 0] + #:result top-of-rocks) + (#:break (> rock-number 2022) [gust (in-cycle gusts)]) + (cond + [(not falling-rock) + (values cave + (~> (first rock-cycle) (place-new-rock top-of-rocks) (gust-move gust cave)) + top-of-rocks + (rest rock-cycle) + (add1 rock-number))])) diff --git a/racket/aoc2022/day-17/rock-shapes b/racket/aoc2022/day-17/rock-shapes new file mode 100644 index 0000000..fbcc382 --- /dev/null +++ b/racket/aoc2022/day-17/rock-shapes @@ -0,0 +1,17 @@ +#### + +.#. +### +.#. + +..# +..# +### + +# +# +# +# + +## +## \ No newline at end of file diff --git a/racket/aoc2022/day-18/day-18.rkt b/racket/aoc2022/day-18/day-18.rkt new file mode 100644 index 0000000..157784d --- /dev/null +++ b/racket/aoc2022/day-18/day-18.rkt @@ -0,0 +1,57 @@ +#lang racket + +(require advent-of-code + relation + threading + graph) + +(define positions (~> (fetch-aoc-input (find-session) 2022 18 #:cache #true) (string-split "\n"))) + +(struct posn (x y z) #:transparent) + +(define cubes + (for/list ([cube (in-list positions)]) + (match (string-split cube ",") + [(list (app ->number x) (app ->number y) (app ->number z)) (posn x y z)]))) + +(define cubes-set (list->set cubes)) + +(define (neighbors p) + (match-define (posn x y z) p) + (for*/list ([dx '(-1 0 1)] + [dy '(-1 0 1)] + [dz '(-1 0 1)] + #:when (= 1 (+ (abs dx) (abs dy) (abs dz)))) + (posn (+ x dx) (+ y dy) (+ z dz)))) + +;; part 1 + +(for*/sum ([cube (in-set cubes-set)] + [neighbor (in-list (neighbors cube))] + #:unless (set-member? cubes-set neighbor)) + 1) + +;; part 2 +(define max-x (~> cubes (apply max _ #:key posn-x) posn-x (+ 2))) +(define max-y (~> cubes (apply max _ #:key posn-y) posn-y (+ 2))) +(define max-z (~> cubes (apply max _ #:key posn-z) posn-z (+ 2))) + +(define air-set + (for*/set ([x (in-inclusive-range -1 max-x)] + [y (in-inclusive-range -1 max-y)] + [z (in-inclusive-range -1 max-z)] + #:do [(define p (posn x y z))] + #:unless (set-member? cubes-set p)) + p)) + +(define air-graph + (for*/lists (ps #:result (undirected-graph ps)) + ([a (in-set air-set)] + [neighbor (in-list (neighbors a))] + #:when (set-member? air-set neighbor)) + (list a neighbor))) + +(for*/sum ([air (in-set (~> air-graph cc (sort > #:key length _) car))] + [neighbor (in-list (neighbors air))] + #:when (set-member? cubes-set neighbor)) + 1) diff --git a/racket/aoc2022/day-19/day-19.rkt b/racket/aoc2022/day-19/day-19.rkt new file mode 100644 index 0000000..1400bf2 --- /dev/null +++ b/racket/aoc2022/day-19/day-19.rkt @@ -0,0 +1,11 @@ +#lang racket + +(require advent-of-code + threading) + +(struct blueprint (id ore clay obsidian geode)) + +(define (parse-line str) + (match (~> str (string-replace #px"[^\\d\\s]" "") string-split) + [(list id ore clay obsidian-ore obsidian-clay geode-ore geode-obsidian) + (blueprint id ore clay (cons obsidian-ore obsidian-clay) (cons geode-ore geode-obsidian))])) diff --git a/racket/aoc2022/day-20/day-20.rkt b/racket/aoc2022/day-20/day-20.rkt new file mode 100644 index 0000000..6dd1070 --- /dev/null +++ b/racket/aoc2022/day-20/day-20.rkt @@ -0,0 +1,48 @@ +#lang racket +(require advent-of-code + threading) + +(define data (port->list read (open-aoc-input (find-session) 2022 20 #:cache #true))) + +(define gps-lst data) +(define gps-len (length gps-lst)) +(define gps-indexed (map cons (inclusive-range 1 gps-len) gps-lst)) + +(define (mix pt data) + (match-define (list left ... (== pt) right ...) data) + (define start (index-of data pt)) + (define move-by (modulo (cdr pt) (sub1 gps-len))) + (cond + [(= 0 move-by) data] + [(<= move-by (length right)) + (match-define-values (new-left new-right) + (split-at (append left right) (modulo (+ move-by start) (sub1 gps-len)))) + (append new-left (list pt) new-right)] + [else + (match-define-values (new-left new-right) + (split-at (append left right) (modulo (+ move-by start) (sub1 gps-len)))) + (append new-left (list pt) new-right)])) + +(define (mix-gps data original) + (for/fold ([pts data]) ([pt original]) + (mix pt pts))) + +(define (cycle-mixed-gps mixed) + (define lst (map cdr mixed)) + (in-sequences (drop lst (index-of lst 0)) (in-cycle lst))) + +(define (calculate-answer seq) + (for/sum ([id '(1000 2000 3000)]) (sequence-ref seq id))) + +;; part 1 +(~> gps-indexed (mix-gps _ gps-indexed) cycle-mixed-gps calculate-answer) + +;; part 2 +(define encrypted-gps-indexed + (for/list ([pt (in-list gps-indexed)] #:do [(match-define (cons i v) pt)]) + (cons i (* 811589153 v)))) + +(~>> encrypted-gps-indexed + ((λ (data) (foldr (λ (_ pts) (mix-gps pts data)) data (range 10)))) + cycle-mixed-gps + calculate-answer) diff --git a/racket/aoc2022/day-21/day-21.rkt b/racket/aoc2022/day-21/day-21.rkt new file mode 100644 index 0000000..fccd6ad --- /dev/null +++ b/racket/aoc2022/day-21/day-21.rkt @@ -0,0 +1,43 @@ +#lang racket + +(require advent-of-code + (only-in relation ->number ->symbol)) + +(struct monkey (name op) #:transparent) +(struct op (f first second) #:transparent) + +(define (parse-monkey str) + (match (string-split str " ") + [(list (app (curryr string-trim ":") name) name1 (app ->symbol f) name2) + (monkey name (op f name1 name2))] + [(list (app (curryr string-trim ":") name) (app ->number int)) + (monkey name (op 'constant int #f))])) + +(define raw-monkeys (port->lines (open-aoc-input (find-session) 2022 21 #:cache #true))) + +(define monkey-table + (for/hash ([m raw-monkeys] #:do [(match-define (monkey name op) (parse-monkey m))]) + (values name op))) + +;; part 1 +(define (evaluate-monkey m-name [guess #f]) + (match-define (op f name1 name2) + (if (and guess (equal? m-name "humn")) (op 'constant guess #f) (hash-ref monkey-table m-name))) + (match f + ['constant name1] + ['+ (+ (evaluate-monkey name1 guess) (evaluate-monkey name2 guess))] + ['- (- (evaluate-monkey name1 guess) (evaluate-monkey name2 guess))] + ['* (* (evaluate-monkey name1 guess) (evaluate-monkey name2 guess))] + ['/ (/ (evaluate-monkey name1 guess) (evaluate-monkey name2 guess))])) + +(evaluate-monkey "root") + +;; part 2 +;; since humn only ever appears once, and it's never the divisor in a division operation, +;; the difference of the branches is linearly proportional to humn +;; therefore, if we find two points we can calculate the root directly +(match-define (op _ branch-1 branch-2) (hash-ref monkey-table "root")) +(define known-side (evaluate-monkey branch-2)) +(define humn-zero (- known-side (evaluate-monkey branch-1 0))) +(define humn-one (- known-side (evaluate-monkey branch-1 1))) +(- (/ humn-zero (- humn-one humn-zero))) diff --git a/racket/aoc2022/day-22/day-22.rkt b/racket/aoc2022/day-22/day-22.rkt new file mode 100644 index 0000000..bcce5f8 --- /dev/null +++ b/racket/aoc2022/day-22/day-22.rkt @@ -0,0 +1,35 @@ +#lang racket + +(require advent-of-code + threading) + +(struct posn (x y) #:transparent) + +(match-define (list raw-map raw-instructions) + (string-split (fetch-aoc-input (find-session) 2022 22 #:cache #true) "\n\n")) + +(define board-map + (for*/hash ([(row y) (in-indexed (string-split raw-map "\n"))] + [(col x) (in-indexed row)] + #:unless (equal? col #\space)) + (define tile + (match col + [#\. 'open] + [#\# 'wall])) + (values (posn x y) tile))) + +(define instructions + (~>> raw-instructions + (regexp-match* #px"(\\d+)|R|L") + (map (match-lambda + [(? string->number n) (string->number n)] + ["R" 'clockwise] + ["L" 'counterclockwise])))) + +(define start-tile + (~>> board-map + hash-keys + (filter (match-lambda + [(posn _ 0) #true] + [_ #false])) + (argmin posn-x))) \ No newline at end of file diff --git a/racket/aoc2022/day-23/day-23.rkt b/racket/aoc2022/day-23/day-23.rkt new file mode 100644 index 0000000..6069859 --- /dev/null +++ b/racket/aoc2022/day-23/day-23.rkt @@ -0,0 +1,76 @@ +#lang racket + +(require advent-of-code + fancy-app + threading) + +(struct posn (x y) #:transparent) + +(define initial-map + (for*/hash ([(row y) (in-indexed (in-lines (open-aoc-input (find-session) 2022 23 #:cache #true)))] + [(col x) (in-indexed row)] + #:when (equal? col #\#)) + (values (posn x y) #t))) + +(define/match (neighbors-in direction p) + [('north (posn x (app sub1 y*))) (list (posn (sub1 x) y*) (posn x y*) (posn (add1 x) y*))] + [('south (posn x (app add1 y*))) (list (posn (sub1 x) y*) (posn x y*) (posn (add1 x) y*))] + [('east (posn (app add1 x*) y)) (list (posn x* (add1 y)) (posn x* y) (posn x* (sub1 y)))] + [('west (posn (app sub1 x*) y)) (list (posn x* (add1 y)) (posn x* y) (posn x* (sub1 y)))]) + +(define/match (move-to direction p) + [('stay p) p] + [('north (posn x y)) (posn x (sub1 y))] + [('south (posn x y)) (posn x (add1 y))] + [('east (posn x y)) (posn (add1 x) y)] + [('west (posn x y)) (posn (sub1 x) y)]) + +(define (propose-movements elves dirs) + (for/hash ([(elf _) (in-hash elves)]) + (define dir-candidates + (for/list ([dir dirs] + #:do [(define neighbors (neighbors-in dir elf))] + #:unless (ormap (curry hash-has-key? elves) neighbors)) + dir)) + (define chosen-dir + (match dir-candidates + ['() 'stay] + [(== dirs) 'stay] + [(cons dir _) dir])) + (values elf chosen-dir))) + +(define (try-proposed-movements elves) + (define moved-elves (make-hash)) + (for ([(elf dir) (in-hash elves)]) + (hash-update! moved-elves (move-to dir elf) (cons elf _) '())) + (define reconciled-elves (make-hash)) + (for ([(posn elves) (in-hash moved-elves)]) + (match elves + ; if there's only one elf at a coordinate, leave it there + [(list _) (hash-set! reconciled-elves posn #t)] + ; if there's many elves at one coordinate, back them up to their previous spot + [many-elves + (for ([elf (in-list many-elves)]) + (hash-set! reconciled-elves elf #t))])) + reconciled-elves) + +;; part 1 +(define (count-empty-spots elves) + (define elf-posns (hash-keys elves)) + (match-define (list x-min _ ... x-max) (sort (map posn-x elf-posns) <)) + (match-define (list y-min _ ... y-max) (sort (map posn-y elf-posns) <)) + (for*/sum ([y (inclusive-range y-min y-max)] [x (inclusive-range x-min x-max)]) + (if (hash-has-key? elves (posn x y)) 0 1))) + +(for/fold ([elves initial-map] [dirs '(north south west east)] #:result (count-empty-spots elves)) + ([_rnd (in-range 10)]) + (values (~> elves (propose-movements dirs) try-proposed-movements) + (append (cdr dirs) (list (car dirs))))) + +;; part 2 +(for/fold ([elves initial-map] [dirs '(north south west east)] [rnd 1] #:result rnd) + ([_rnd (in-naturals)]) + (define elves-proposed (propose-movements elves dirs)) + ; elves have stopped moving if they all conclude they want to stay put + #:break (~> elves-proposed hash-values remove-duplicates (equal? '(stay))) + (values (try-proposed-movements elves-proposed) (append (cdr dirs) (list (car dirs))) (add1 rnd))) diff --git a/racket/aoc2022/day-25/day-25.rkt b/racket/aoc2022/day-25/day-25.rkt new file mode 100644 index 0000000..078cef4 --- /dev/null +++ b/racket/aoc2022/day-25/day-25.rkt @@ -0,0 +1,24 @@ +#lang racket +(require advent-of-code + threading) + +(define fuel-requirements (port->lines (open-aoc-input (find-session) 2022 25 #:cache #true))) + +(define (snafu->decimal snafu) + (for/sum ([digit (in-list (~> snafu string->list reverse))] [place (in-naturals)]) + (define value + (match digit + [#\= -2] + [#\- -1] + [c (~> c string string->number)])) + (* value (expt 5 place)))) + +(define (decimal->snafu n [acc '()]) + (match n + [0 (list->string acc)] + [n (decimal->snafu (quotient (+ n 2) 5) (cons (string-ref "012=-" (modulo n 5)) acc))])) + +;; part 1 +(~> (for/sum ([fuel (in-list fuel-requirements)]) (snafu->decimal fuel)) decimal->snafu) + +;; no part 2 メリークリスマス \ No newline at end of file diff --git a/racket/aoc2023/day-01/day-01.rkt b/racket/aoc2023/day-01/day-01.rkt new file mode 100644 index 0000000..b720f79 --- /dev/null +++ b/racket/aoc2023/day-01/day-01.rkt @@ -0,0 +1,38 @@ +#lang racket + +(require advent-of-code + threading) + +(define calibration-values (fetch-aoc-input (find-session) 2023 1)) + +(define (to-number str) + (match (string->number str) + [#false (hash-ref word-to-digit str)] + [n n])) + +(define (parse-calibration-value v valid) + (for/fold ([acc '()] [value v] #:result (+ (to-number (first acc)) (* 10 (to-number (last acc))))) + ([_ (in-naturals)]) + #:break (equal? value "") + (let ([possible-prefix (findf (curry string-prefix? value) valid)]) + (if possible-prefix + (values (cons possible-prefix acc) (substring value 1)) + (values acc (substring value 1)))))) + +(define (total-calibration input valid) + (~> input + (string-trim) + (string-split "\n") + (map (λ~> (parse-calibration-value valid)) _) + (apply + _))) + +;; part 1 + +(define valid-for-part-1 (~> (range 1 10) (map ~a _))) +(total-calibration calibration-values valid-for-part-1) + +;; part 2 +(define word-to-digit + (hash "one" 1 "two" 2 "three" 3 "four" 4 "five" 5 "six" 6 "seven" 7 "eight" 8 "nine" 9)) +(define valid-for-part-2 (append valid-for-part-1 (hash-keys word-to-digit))) +(total-calibration calibration-values valid-for-part-2) diff --git a/racket/aoc2023/day-02/day-02-parser.rkt b/racket/aoc2023/day-02/day-02-parser.rkt new file mode 100644 index 0000000..76cc24f --- /dev/null +++ b/racket/aoc2023/day-02/day-02-parser.rkt @@ -0,0 +1,55 @@ +#lang racket + +(require racket/hash + advent-of-code + data/applicative + data/either + data/monad + megaparsack + megaparsack/text + threading) + +(struct game (id r g b)) + +(define cube/p + (do [n <- integer/p] + space/p + [c <- (or/p (string/p "red") + (string/p "blue") + (string/p "green"))] + (pure (cons c n)))) + +(define draw/p + (do [xs <- (many/p cube/p #:min 1 #:max 3 #:sep (string/p ", "))] + (pure (apply hash (flatten xs))))) + +(define all-draws/p + (do (string/p "Game ") + [id <- integer/p] + (string/p ": ") + [all-draws <- (many/p draw/p #:min 1 #:sep (string/p "; "))] + (define maxima + (foldl (curry hash-union #:combine max) + (hash "red" 0 "green" 0 "blue" 0) + all-draws)) + (pure (game id + (hash-ref maxima "red") + (hash-ref maxima "green") + (hash-ref maxima "blue"))))) + +(define game-maxima + (~>> (open-aoc-input (find-session) 2023 2) + port->lines + (map (λ~>> (parse-string all-draws/p) + from-either)))) + +;; part 1 +(for/sum ([m (in-list game-maxima)] + #:unless (or (> (game-r m) 12) + (> (game-g m) 13) + (> (game-b m) 14))) + (game-id m)) + +;; part 2 +(for/sum ([m (in-list game-maxima)]) + (* (game-r m) (game-g m) (game-b m))) diff --git a/racket/aoc2023/day-02/day-02.rkt b/racket/aoc2023/day-02/day-02.rkt new file mode 100644 index 0000000..973d20c --- /dev/null +++ b/racket/aoc2023/day-02/day-02.rkt @@ -0,0 +1,35 @@ +#lang racket + +(require advent-of-code) + +(struct roll (id red green blue)) + +(define all-games + (for/list ([raw-game (in-list (port->lines (open-aoc-input (find-session) 2023 2)))] + #:do [(define game (string-trim raw-game "Game ")) + (match-define (list id trials) (string-split game ": "))]) + (for/list ([trial (in-list (string-split trials "; "))]) + (for/fold ([acc (roll (string->number id) 0 0 0)]) + ([color (in-list (string-split trial ", "))]) + (match (string-split color) + [(list (app string->number n) "red") (struct-copy roll acc [red n])] + [(list (app string->number n) "green") (struct-copy roll acc [green n])] + [(list (app string->number n) "blue") (struct-copy roll acc [blue n])]))))) + +;; part 1 +(for/sum ([game (in-list all-games)] + #:when (andmap (λ (g) (and ((roll-red g) . <= . 12) + ((roll-green g) . <= . 13) + ((roll-blue g) . <= . 14))) + game)) + (roll-id (first game))) + +;; part 2 +(for/sum ([game (in-list all-games)]) + (define max-cubes + (for/fold ([acc (roll #f 0 0 0)]) ([r (in-list game)]) + (roll #f + (max (roll-red acc) (roll-red r)) + (max (roll-green acc) (roll-green r)) + (max (roll-blue acc) (roll-blue r))))) + (* (roll-red max-cubes) (roll-green max-cubes) (roll-blue max-cubes))) diff --git a/racket/aoc2023/day-03/day-03.rkt b/racket/aoc2023/day-03/day-03.rkt new file mode 100644 index 0000000..60e81a6 --- /dev/null +++ b/racket/aoc2023/day-03/day-03.rkt @@ -0,0 +1,72 @@ +#lang racket + +(require advent-of-code + threading) + +(struct posn (x y) #:transparent) +(struct part (n posns) #:transparent) + +(define (make-board port) + (for*/hash ([(row y) (in-indexed (port->lines port))] + [(col x) (in-indexed (string->list row))] + #:unless (equal? col #\.)) + (define v + (cond + [(string->number (string col))] + [(equal? col #\*) 'gear] + [else 'other])) + (values (posn x y) v))) + +(define board (~> (open-aoc-input (find-session) 2023 3 #:cache #true) make-board)) + +(define (posn (for/hash ([(k v) (in-hash b)] #:when (f v)) + (values k v)) + hash->list + (sort posn> part-posns (append-map neighbors) remove-duplicates)) +(define (symbol-in-neighbors b pt acc) + (~>> pt + to-neighbors + (ormap (λ (p) (let ([lookup (hash-ref b p #f)]) + (or (equal? lookup 'gear) (equal? lookup 'other))))) + ((λ (bool) (if bool (+ acc (part-n pt)) acc))))) + +;; part 1 +(define parts (~>> board (find-cells integer?) group-into-parts)) +(foldl (curry symbol-in-neighbors board) 0 parts) + +;; part 2 +(define gears (~>> board (find-cells (curry equal? 'gear)) (map car))) +(define parts-with-neighbors (map (λ (pt) (struct-copy part pt [posns (to-neighbors pt)])) parts)) + +(define (find-parts-near-gear pts gear) + (filter-map (λ (pt) (and (findf (curry equal? gear) (part-posns pt)) (part-n pt))) pts)) + +(~>> gears + (filter-map (λ~>> (find-parts-near-gear parts-with-neighbors) + ((λ (ns) (if (= (length ns) 2) (* (first ns) (second ns)) #f))))) + (apply +)) diff --git a/racket/aoc2023/day-04/day-04.rkt b/racket/aoc2023/day-04/day-04.rkt new file mode 100644 index 0000000..7a357c5 --- /dev/null +++ b/racket/aoc2023/day-04/day-04.rkt @@ -0,0 +1,40 @@ +#lang racket + +(require advent-of-code + data/applicative + data/either + data/monad + megaparsack + megaparsack/text + threading) + +(struct card (n wins)) + +(define card/p + (do (string/p "Card") + (many/p space/p) + [n <- integer/p] + (string/p ":") + (many/p space/p) + [winners <- (many-until/p integer/p #:sep (many/p space/p) #:end (try/p (string/p " | ")))] + (many/p space/p) + [has <- (many+/p integer/p #:sep (many/p space/p))] + (pure (card n (set-count (set-intersect (first winners) has)))))) + +(define raw-cards (~> (open-aoc-input (find-session) 2023 4 #:cache #true) port->lines)) + +;; part 1 +(for/sum ([raw-card (in-list raw-cards)] + #:do [(match-define (success (card _ wins)) (parse-string card/p raw-card))] + #:unless (= wins 0)) + (expt 2 (sub1 wins))) + +;; part 2 +(for/fold ([counts (for/hash ([n (in-inclusive-range 1 (length raw-cards))]) + (values n 1))] + #:result (apply + (hash-values counts))) + ([raw-card (in-list raw-cards)] + #:do [(match-define (success (card n wins)) (parse-string card/p raw-card))]) + (define bonus-range (inclusive-range (+ n 1) (+ n wins))) + (define won-cards (hash-ref counts n)) + (foldl (λ (n acc) (hash-update acc n (curry + won-cards))) counts bonus-range)) diff --git a/racket/aoc2023/day-05/day-05.rkt b/racket/aoc2023/day-05/day-05.rkt new file mode 100644 index 0000000..5b9aa52 --- /dev/null +++ b/racket/aoc2023/day-05/day-05.rkt @@ -0,0 +1,91 @@ +#lang racket + +(require advent-of-code + algorithms + threading) + +(struct map-range (start end offset)) +(struct seed-range (start end)) + +(define input (fetch-aoc-input (find-session) 2023 5 #:cache #true)) + +(match-define (list* raw-seeds raw-mappings) (string-split input "\n\n")) + +(define seeds-naive (~> raw-seeds (string-split " ") rest (map string->number _))) +(define mappers + (for/list ([raw-mapping (in-list raw-mappings)]) + (for/lists (map-ranges #:result (sort map-ranges < #:key map-range-start)) + ([raw-map-range (in-list (rest (string-split raw-mapping "\n")))] + #:do [(match-define (list dest source width) + (~> raw-map-range (string-split _ " ") (map string->number _)))]) + (map-range source (+ source width -1) (- dest source))))) + +;; part 1 +(define (in-map-range? n mr) + (<= (map-range-start mr) n (map-range-end mr))) + +(define (transform-value mapper n) + (match mapper + ['() n] + [(list* mr _) + #:when (in-map-range? n mr) + (+ n (map-range-offset mr))] + [(list* _ rest-mapper) (transform-value rest-mapper n)])) + +(for/lists (transforms #:result (apply min transforms)) + ([seed (in-list seeds-naive)]) + (foldl transform-value seed mappers)) + +;; part 2 +(define (remap-range r mapper [acc '()]) + (match-define (seed-range r-start r-end) r) + (match mapper + ; mapper exhausted + ['() (cons r acc)] + ; range to the left - not covered by this mapping, so keep as-is + [(list* (map-range m-start _ _) _) + #:when (< r-end m-start) + (cons r acc)] + ; range to the right - move to next map-range + [(list* (map-range _ m-end _) ms) + #:when (< m-end r-start) + (remap-range r ms acc)] + ; range is inside map-range - transform whole range + [(list* (map-range m-start m-end offset) _) + #:when (and (<= m-start r-start) (<= r-end m-end)) + (cons (seed-range (+ r-start offset) (+ r-end offset)) acc)] + ; range overlaps start only - keep left side, transform right side + [(list* (map-range m-start m-end offset) ms) + #:when (and (< r-start m-start) (<= r-end m-end)) + (remap-range (seed-range (add1 m-end) r-end) + ms + (cons (seed-range (+ m-start offset) (+ r-end offset)) acc))] + ; range overlaps end - transform left side, pass right side + [(list* (map-range m-start m-end offset) ms) + #:when (and (< m-start r-start) (<= m-end r-end)) + (remap-range (seed-range (add1 m-end) r-end) + ms + (cons (seed-range (+ r-start offset) (+ m-end offset)) acc))] + ; range overlaps whole map-range - keep left side, transform middle, pass right side + [(list* (map-range m-start m-end offset) ms) + (remap-range (seed-range (add1 m-end) r-end) + ms + (cons (seed-range (+ m-start offset) (+ m-end offset)) + (cons (seed-range (add1 m-end) r-end) acc)))])) + +(define (remap-ranges rs mappers) + (cond + [(empty? mappers) rs] + [else + (~>> rs (append-map (curryr remap-range (first mappers))) (remap-ranges _ (rest mappers)))])) + +(~> seeds-naive + (chunks-of 2) + (map (λ (xs) + (match-define (list start width) xs) + (~> (list (seed-range start (+ start width -1))) + (remap-ranges mappers) + (argmin seed-range-start _))) + _) + (argmin seed-range-start _) + seed-range-start) diff --git a/racket/aoc2023/day-06/day-06.rkt b/racket/aoc2023/day-06/day-06.rkt new file mode 100644 index 0000000..53ca9ee --- /dev/null +++ b/racket/aoc2023/day-06/day-06.rkt @@ -0,0 +1,32 @@ +#lang racket + +(require advent-of-code + threading) + +(match-define (list times distances) + (~> (open-aoc-input (find-session) 2023 6 #:cache #true) port->lines)) + +;; part 1 +(define get-numbers (λ~> string-split (map string->number _) rest)) +(define (find-bound race-time dist button-time step) + (if (< dist (* button-time (- race-time button-time))) + button-time + (find-bound race-time dist (+ step button-time) step))) + +(define (lower-bound rtime dist) + (find-bound rtime dist 1 1)) +(define (upper-bound rtime dist) + (find-bound rtime dist rtime -1)) + +(for/fold ([acc 1]) + ([race-time (in-list (get-numbers times))] + [distance (in-list (get-numbers distances))]) + (* acc (add1 (- (upper-bound race-time distance) (lower-bound race-time distance))))) + +;; part 2 + +(define unkern (λ~>> get-numbers (apply ~a) string->number)) +(define big-time (unkern times)) +(define big-distance (unkern distances)) + +(add1 (- (upper-bound big-time big-distance) (lower-bound big-time big-distance))) diff --git a/racket/aoc2023/day-07/day-07.rkt b/racket/aoc2023/day-07/day-07.rkt new file mode 100644 index 0000000..30e629b --- /dev/null +++ b/racket/aoc2023/day-07/day-07.rkt @@ -0,0 +1,82 @@ +#lang racket + +(require advent-of-code + threading + memo) + +(struct hand (cards wager)) + +(define/match (card->int card) + [((? char-numeric?)) (~> card string string->number)] + [(#\A) 14] + [(#\K) 13] + [(#\Q) 12] + [(#\J) 11] + [(#\T) 10] + [(#\*) 1]) + +(define (parse-hand str #:jokers [jokers? #f]) + (match-define (list card-str wager-str) (string-split str)) + (define cards + (~> card-str + ((λ (str) (if jokers? (string-replace str "J" "*") str))) + string->list + (map card->int _))) + (define wager (~> wager-str string->number)) + (hand cards wager)) + +(define input (~> (open-aoc-input (find-session) 2023 7 #:cache #true) port->lines)) + +(define/memoize (identify-hand h) + (define freqs (~> h hand-cards (sort <) (group-by identity _) (map length _))) + (match freqs + [(list-no-order 5) 8] + [(list-no-order 1 4) 7] + [(list-no-order 2 3) 6] + [(list-no-order 1 1 3) 5] + [(list-no-order 1 2 2) 4] + [(list-no-order 1 1 1 2) 3] + [(list-no-order 1 1 1 1 1) 2] + [_ 1])) + +(define (compare-first-card cs1 cs2) + (if (= (first cs1) (first cs2)) + (compare-first-card (rest cs1) (rest cs2)) + (< (first cs1) (first cs2)))) + +(define (compare-hands with h1 h2) + (define rank1 (with h1)) + (define rank2 (with h2)) + (if (= rank1 rank2) (compare-first-card (hand-cards h1) (hand-cards h2)) (< rank1 rank2))) + +;; part 1 + +(define (compare-hands-no-wilds h1 h2) + (compare-hands identify-hand h1 h2)) + +(define (total-score in #:jokers [jokers? #false]) + (define sorted-hands + (~> in + (map (curry parse-hand #:jokers jokers?) _) + (sort (if jokers? compare-hands-no-wilds compare-hands-with-wilds)))) + (for/sum ([(h i) + (in-indexed sorted-hands)]) + (* (add1 i) (hand-wager h)))) + +(total-score input) + +;; part 2 + +(define/memoize (find-best-joker-substitution h) + (for/fold ([best-hand (hand '() 0)]) + ([wild (in-inclusive-range 2 14)]) + (define trial-hand + (hand (map (λ (c) (if (= c 1) wild c)) (hand-cards h)) (hand-wager h))) + (if (> (identify-hand trial-hand) (identify-hand best-hand)) + trial-hand + best-hand))) + +(define (compare-hands-with-wilds h1 h2) + (compare-hands (λ~> find-best-joker-substitution identify-hand) h1 h2)) + +(total-score input #:jokers #true) diff --git a/racket/aoc2023/day-08/day-08.rkt b/racket/aoc2023/day-08/day-08.rkt new file mode 100644 index 0000000..06daafa --- /dev/null +++ b/racket/aoc2023/day-08/day-08.rkt @@ -0,0 +1,36 @@ +#lang racket + +(require advent-of-code + threading) + +(struct exits (left right) #:transparent) + +(match-define (list raw-directions raw-maze) + (~> (fetch-aoc-input (find-session) 2023 8 #:cache #true) (string-split "\n\n"))) + +(define directions (string->list raw-directions)) + +(define maze + (for/hash ([line (in-list (string-split raw-maze "\n"))]) + (match (regexp-match #rx"(...) = \\((...), (...)\\)" line) + [(list _ name left right) (values name (exits left right))]))) + +(define (to-next-node start end dirs maze) + (for/fold ([current start] + [acc 0] + #:result acc) + ([dir (in-cycle dirs)]) + #:break (string-suffix? current end) + (define node (hash-ref maze current)) + (case dir + [(#\L) (values (exits-left node) (add1 acc))] + [(#\R) (values (exits-right node) (add1 acc))]))) + +;; part 1 +(to-next-node "AAA" "ZZZ" directions maze) + +;; part 2 +(for/lists (ns #:result (apply lcm ns)) + ([start (in-list (hash-keys maze))] + #:when (string-suffix? start "A")) + (to-next-node start "Z" directions maze)) diff --git a/racket/aoc2023/day-09/day-09-polynomial.rkt b/racket/aoc2023/day-09/day-09-polynomial.rkt new file mode 100644 index 0000000..5bacb1f --- /dev/null +++ b/racket/aoc2023/day-09/day-09-polynomial.rkt @@ -0,0 +1,17 @@ +#lang racket + +(require advent-of-code + threading + simple-polynomial/tools) + +(define histories + (for/list ([raw-history (in-lines (open-aoc-input (find-session) 2023 9 #:cache #true))]) + (~>> raw-history + string-split + (map string->number)))) + +(for/lists (left right #:result (cons (apply + left) (apply + right))) + ([history (in-list histories)]) + (define f (interpolate-at-integer-points history)) + (values (f -1) + (f (length history)))) diff --git a/racket/aoc2023/day-09/day-09.rkt b/racket/aoc2023/day-09/day-09.rkt new file mode 100644 index 0000000..5eda1eb --- /dev/null +++ b/racket/aoc2023/day-09/day-09.rkt @@ -0,0 +1,32 @@ +#lang racket + +(require advent-of-code + threading) + +(define histories + (for/list ([raw-history (in-lines (open-aoc-input (find-session) 2023 9 #:cache #true))]) + (~>> raw-history + string-split + (map string->number)))) + +(define (constant? xs) + (= 1 (length (remove-duplicates xs)))) + +(define/match (derivative xs) + [((list a b)) (list (- b a))] + [((list* a b _)) (cons (- b a) (derivative (rest xs)))]) + +(define (extrapolate xs) + (if (constant? xs) + (car xs) + (+ (last xs) (extrapolate (derivative xs))))) + +;; part 1 +(~>> histories + (map extrapolate) + (apply +)) + +;; part 2 +(~>> histories + (map (λ~> reverse extrapolate)) + (apply +)) diff --git a/racket/aoc2023/day-10/day-10.rkt b/racket/aoc2023/day-10/day-10.rkt new file mode 100644 index 0000000..64d8727 --- /dev/null +++ b/racket/aoc2023/day-10/day-10.rkt @@ -0,0 +1,97 @@ +#lang racket + +(require advent-of-code + threading) + +(struct posn (r c) #:transparent) + +(define/match (add-posns _p1 _p2) + [((posn x1 y1) (posn x2 y2)) (posn (+ x1 x2) (+ y1 y2))]) + +(define go-north (posn -1 0)) +(define go-south (posn 1 0)) +(define go-east (posn 0 1)) +(define go-west (posn 0 -1)) + +(define initial-directions + (list (cons go-north '(#\| #\7 #\F)) + (cons go-south '(#\| #\J #\L)) + (cons go-east '(#\- #\J #\7)) + (cons go-west '(#\- #\F #\L)))) + +(define/match (pipe-neighbors _pipe) + [(#\|) (list go-north go-south)] + [(#\-) (list go-east go-west)] + [(#\L) (list go-north go-east)] + [(#\F) (list go-south go-east)] + [(#\7) (list go-south go-west)] + [(#\J) (list go-north go-west)]) + +(define (make-pipe-grid in) + (for*/hash ([(row r) (in-indexed (string-split in "\n"))] + [(ch c) (in-indexed (string->list row))]) + (values (posn (add1 r) (add1 c)) ch))) + +(define (get-valid-S-neighbors S grid) + (for/list ([dir (in-list initial-directions)] + #:do [(match-define (cons d valid) dir)] + #:do [(define neighbor (add-posns d S))] + #:when (member (hash-ref grid neighbor 'none) valid)) + neighbor)) + +(define (to-next-pipe current previous grid [acc '()]) + (cond + [(equal? (hash-ref grid current #f) #\S) acc] + [else + (define next + (for/first ([d (in-list (pipe-neighbors (hash-ref grid current)))] + #:do [(define neighbor (add-posns d current))] + #:unless (equal? neighbor previous)) + neighbor)) + (~> next (to-next-pipe _ current grid (cons current acc)))])) + +;; part 1 +(define input (fetch-aoc-input (find-session) 2023 10 #:cache #true)) + +(define pipe-grid (make-pipe-grid input)) + +(define S-posn + (for/first ([(k v) (in-hash pipe-grid)] #:when (equal? v #\S)) + k)) + +(define S-neighbors (get-valid-S-neighbors S-posn pipe-grid)) + +(define pipe-loop (to-next-pipe (first S-neighbors) S-posn pipe-grid '())) + +(/ (add1 (length pipe-loop)) 2) + +;; part 2 +(define pipe-loop-set (~> (list->set pipe-loop) (set-add S-posn))) + +(define (trace-rays pt pipes grid) + (cond + [(set-member? pipes pt) #f] + [else (odd? (trace-ray pt pipes grid))])) + +(define (trace-ray pt pipes grid) + (define row (posn-r pt)) + (for/fold ([acc 0] + [corner #f] + #:result acc) + ([col (in-naturals (posn-c pt))] + #:do [(define test-pt (posn row col))] + #:break (not (hash-has-key? grid test-pt)) + #:when (set-member? pipes test-pt)) + (define pipe (hash-ref grid test-pt)) + (match* (corner pipe) + [(#f #\|) (values (add1 acc) #f)] ; vertical crossing + [(#f (or #\F #\L)) (values acc pipe)] + [(#\F #\J) (values (add1 acc) #f)] ; a ┏━┛ shape counts as a vertical crossing + [(#\L #\7) (values (add1 acc) #f)] + [(#\F #\7) (values acc #f)] ; a ┏━┓ shape doesn't count + [(#\L #\J) (values acc #f)] + [(_ _) (values acc corner)]))) + +(~> pipe-grid + hash-keys + (count (λ~> (trace-rays pipe-loop-set pipe-grid)) _)) diff --git a/racket/aoc2023/day-11/day-11.rkt b/racket/aoc2023/day-11/day-11.rkt new file mode 100644 index 0000000..dba617b --- /dev/null +++ b/racket/aoc2023/day-11/day-11.rkt @@ -0,0 +1,40 @@ +#lang racket + +(require advent-of-code + threading) + +(struct posn (x y) #:transparent) + +(define input + (~> (fetch-aoc-input (find-session) 2023 11 #:cache #true) + (string-split "\n") + (map string->list _))) + +(define (get-empty-ranks grid) + (for/list ([(rank n) (in-indexed grid)] #:when (equal? '(#\.) (remove-duplicates rank))) + n)) + +(define (count-prior-empty-ranks rank empty-ranks) + (~> empty-ranks + (takef (curryr < rank)) + length)) + +(define empty-rows (get-empty-ranks input)) +(define empty-columns (get-empty-ranks (apply map list input))) ;; transpose + +(define (sum-of-star-distances in expand-by) + (define stars + (for*/list ([(row x) (in-indexed in)] + [(col y) (in-indexed row)] + #:when (equal? col #\#)) + (posn (+ x (* (sub1 expand-by) (count-prior-empty-ranks x empty-rows))) + (+ y (* (sub1 expand-by) (count-prior-empty-ranks y empty-columns)))))) + (for/sum ([star-pair (in-combinations stars 2)]) + (match-define (list (posn x1 y1) (posn x2 y2)) star-pair) + (+ (abs (- x1 x2)) (abs (- y1 y2))))) + +;; part 1 +(sum-of-star-distances input 2) + +;; part 2 +(sum-of-star-distances input 1000000) diff --git a/racket/aoc2023/day-12/day-12.rkt b/racket/aoc2023/day-12/day-12.rkt new file mode 100644 index 0000000..50b14bb --- /dev/null +++ b/racket/aoc2023/day-12/day-12.rkt @@ -0,0 +1,65 @@ +#lang racket + +(require advent-of-code + threading + memo) + +(struct condition (template spring-set)) + +(define conditions + (for/list ([line (in-lines (open-aoc-input (find-session) 2023 12 #:cache #true))]) + (match (string-split line #px"[ ,]") + [(list* template spring-set) + (condition (string->list template) (map string->number spring-set))]))) + +(define/memoize (do-count template spring-group left-in-group need-gap?) + ;; template: list of spring positions + ;; spring-group: list of remaining contiguous groups of damaged springs + ;; left-in-group: springs remaining in current bad spring group being placed + ;; need-gap?: did we just finish placing a bad spring group + ;; and need at least one undamaged spring before starting the next one? + (match* (template spring-group left-in-group need-gap?) + ;; no springs left to place and no places left to place springs + ;; this is an OK arrangement, count it + [('() '() 0 _) 1] + ;; ambiguous wildcard, try both skipping this spot and starting a damaged spring group here + [((list* #\? t-rest) (list* g g-rest) 0 #f) + (+ (do-count t-rest g-rest (sub1 g) (= g 1)) + (do-count t-rest spring-group 0 #f))] + ;; definitely a place for a good spring (.), move on without consuming any spring groups + [((list* #\? t-rest) '() 0 #f) ; good spring, no more damaged springs to place + (do-count t-rest spring-group 0 #f)] + [((list* #\? t-rest) _ 0 #t) ; good spring right after finishing a group of bad springs + (do-count t-rest spring-group 0 #f)] + [((list* #\. t-rest) _ 0 _) ; known good spring + (do-count t-rest spring-group 0 #f)] + ;; start of bad spring (#) group, use the next spring group and remove 1 from it + [((list* #\# t-rest) (list* g g-rest) 0 #f) (do-count t-rest g-rest (sub1 g) (= g 1))] + ;; continuation of bad spring group, same + [((list* (or #\? #\#) t-rest) g left #f) (do-count t-rest g (sub1 left) (= left 1))] + ;; if nothing above works, this arrangement's invalid + [(_ _ _ _) 0])) + +(define (count-solutions c) + (match-define (condition template spring-set) c) + (do-count template spring-set 0 #f)) + +;; part 1 +(for/sum ([c (in-list conditions)]) + (count-solutions c)) + +;; part 2 +(define expanded-conditions + (for/list ([c (in-list conditions)]) + (condition (~> c + condition-template + (make-list 5 _) + (add-between #\?) + flatten) + (~> c + condition-spring-set + (make-list 5 _) + flatten)))) + +(for/sum ([c* (in-list expanded-conditions)]) + (count-solutions c*)) diff --git a/racket/aoc2023/day-13/day-13.rkt b/racket/aoc2023/day-13/day-13.rkt new file mode 100644 index 0000000..47718f8 --- /dev/null +++ b/racket/aoc2023/day-13/day-13.rkt @@ -0,0 +1,47 @@ +#lang racket + +(require advent-of-code + threading) + +(define input + (~>(fetch-aoc-input (find-session) 2023 13 #:cache #true) + (string-split "\n\n") + (map (λ~> string-split) _))) + +(define (do-symmetric? lefts rights errs) + (cond + [(empty? rights) #f] + [else + (define found-errs + (for/sum ([l (in-string (string-join lefts ""))] + [r (in-string (string-join rights ""))] + #:unless (char=? l r)) + 1)) + (if (= errs found-errs) + (length lefts) + (do-symmetric? (cons (first rights) lefts) + (rest rights) + errs))])) + +(define (symmetric? xss errs) + (do-symmetric? (list (first xss)) (rest xss) errs)) + +(define (transpose strs) + (~> strs + (map string->list _) + (apply map list _) + (map list->string _))) + +(define (find-symmetry-score xss errs) + (cond + [(symmetric? xss errs) => (curry * 100)] + [else (symmetric? (transpose xss) errs)])) + +;; part 1 +(for/sum ([note (in-list input)]) + (find-symmetry-score note 0)) + +;; part 2 +(for/sum ([note (in-list input)]) + (find-symmetry-score note 1)) + diff --git a/racket/aoc2023/day-14/day-14.rkt b/racket/aoc2023/day-14/day-14.rkt new file mode 100644 index 0000000..d0b7cad --- /dev/null +++ b/racket/aoc2023/day-14/day-14.rkt @@ -0,0 +1,49 @@ +#lang racket + +(require advent-of-code + threading + "../../jj-aoc.rkt") + +(define input + (~> (fetch-aoc-input (find-session) 2023 14 #:cache #true) + string-split + (map string->list _) + transpose)) + +(define (roll-boulders board) + (for/list ([col (in-list board)]) + (~> col (chunks-by (curry equal? #\#)) (append-map (curryr sort char>?) _)))) + +(define (score board) + (for*/sum ([col (in-list board)] + [(row n) (in-indexed (reverse col))] + #:when (equal? row #\O)) + (add1 n))) + +;; part 1 +(~> input + roll-boulders + score) + +;; part 2 +(define (rotate-board xss) + (~> xss + (map reverse _) + transpose)) + +(define (full-cycle board) + (foldl (λ (_ acc) (~> acc roll-boulders rotate-board)) board (range 4))) + +(define (spin-to-win board) + (define cache (make-hash)) + (define (do-spin board n) + (match (hash-ref cache board 'not-found) + ['not-found + (hash-set! cache board n) + (do-spin (full-cycle board) (sub1 n))] + [seen + (define to-end (modulo n (- seen n))) + (score (foldl (λ (_ acc) (full-cycle acc)) board (range to-end)))])) + (do-spin board 1000000000)) + +(~> input spin-to-win) diff --git a/racket/aoc2023/day-15/day-15.rkt b/racket/aoc2023/day-15/day-15.rkt new file mode 100644 index 0000000..d049565 --- /dev/null +++ b/racket/aoc2023/day-15/day-15.rkt @@ -0,0 +1,41 @@ +#lang racket + +(require advent-of-code + threading) + +(define input + (~> (fetch-aoc-input (find-session) 2023 15 #:cache #true) string-trim (string-split ","))) + +(define (hash-algorithm str) + (for/fold ([acc 0]) ([c (in-string str)]) + (~> c char->integer (+ acc) (* 17) (modulo _ 256)))) + +;; part 1 +(for/sum ([code (in-list input)]) (hash-algorithm code)) + +;; part 2 +(define (remove-lens boxes label) + (hash-update boxes + (hash-algorithm label) + (λ (lens-set) (remove label lens-set (λ (rem l) (equal? rem (car l))))) + '())) + +(define (insert-lens boxes label focal) + (define new-lens (cons label focal)) + (hash-update boxes + (hash-algorithm label) + (λ (lens-set) + (if (assoc label lens-set) + (map (λ (pair) (if (equal? (car pair) label) new-lens pair)) lens-set) + (append lens-set (list new-lens)))) + (list new-lens))) + +(define (focusing-power boxes) + (for*/sum ([(box-number lenses) (in-hash boxes)] [(lens order) (in-indexed lenses)]) + (* (add1 box-number) (add1 order) (cdr lens)))) + +(for/fold ([boxes (hash)] #:result (focusing-power boxes)) ([code (in-list input)]) + (match code + [(regexp #rx"(.*)=(.*)" (list _ label (app string->number focal))) + (insert-lens boxes label focal)] + [(regexp #rx"(.*)-" (list _ label)) (remove-lens boxes label)])) diff --git a/racket/aoc2023/day-16/day-16.rkt b/racket/aoc2023/day-16/day-16.rkt new file mode 100644 index 0000000..4a70de8 --- /dev/null +++ b/racket/aoc2023/day-16/day-16.rkt @@ -0,0 +1,70 @@ +#lang racket + +(require advent-of-code + threading) + +(struct posn (r c) #:transparent) +(struct light (posn dir) #:transparent) + +(define input (fetch-aoc-input (find-session) 2023 16 #:cache #true)) + +(define grid + (for*/hash ([(row r) (in-indexed (string-split input "\n"))] + [(cell c) (in-indexed (in-string row))]) + (values (posn r c) cell))) + +(define/match (move _l) + [((light (posn r c) 'up)) (light (posn (sub1 r) c) 'up)] + [((light (posn r c) 'right)) (light (posn r (add1 c)) 'right)] + [((light (posn r c) 'left)) (light (posn r (sub1 c)) 'left)] + [((light (posn r c) 'down)) (light (posn (add1 r) c) 'down)]) + +(define/match (transform l _cell) + [(_ #\.) l] + [(_ 'off) '()] + + [((light _ (or 'up 'down)) #\|) l] + [((light _ (or 'left 'right)) #\-) l] + + [((light p 'left) #\/) (light p 'down)] + [((light p 'down) #\/) (light p 'left)] + [((light p 'right) #\/) (light p 'up)] + [((light p 'up) #\/) (light p 'right)] + + [((light p 'left) #\\) (light p 'up)] + [((light p 'up) #\\) (light p 'left)] + [((light p 'right) #\\) (light p 'down)] + [((light p 'down) #\\) (light p 'right)] + + [((light p (or 'left 'right)) #\|) (list (light p 'up) (light p 'down))] + [((light p (or 'up 'down)) #\-) (list (light p 'left) (light p 'right))]) + +;; part 1 +(define (get-energized start) + (for/fold ([energized (set)] + [previously-visited (set)] + [beam-tips (set start)] + #:result (set-count energized)) + ([_ (in-naturals)]) + (define next-positions + (list->set + (flatten (for/list ([b (in-set beam-tips)]) + (~> b move ((λ (b) (transform b (hash-ref grid (light-posn b) 'off))))))))) + (define all-visited (set-union previously-visited next-positions)) + (define next-energized (set-union energized (list->set (set-map next-positions light-posn)))) + #:break (equal? previously-visited all-visited) + (values next-energized all-visited (set-subtract next-positions previously-visited)))) + +(get-energized (light (posn 0 -1) 'right)) + +;; part 2 +(define rows (~> input (string-split "\n") length)) +(define cols (~> input (string-split #rx"\n.*") first string-length)) + +(define all-starting-positions + (append (map (λ (r) (light (posn r -1) 'right)) (range rows)) + (map (λ (r) (light (posn r cols) 'left)) (range rows)) + (map (λ (c) (light (posn -1 c) 'down)) (range cols)) + (map (λ (c) (light (posn rows c) 'up)) (range cols)))) + +(get-energized (argmax get-energized all-starting-positions)) \ No newline at end of file diff --git a/racket/aoc2023/day-17/day-17.rkt b/racket/aoc2023/day-17/day-17.rkt new file mode 100644 index 0000000..05709ad --- /dev/null +++ b/racket/aoc2023/day-17/day-17.rkt @@ -0,0 +1,86 @@ +#lang racket + +(require advent-of-code + threading + data/heap) + +(struct state (p heat-lost previous history)) +(struct posn (r c)) + +(define/match (add _p1 _p2) + [((posn r1 c1) (posn r2 c2)) (posn (+ r1 r2) (+ c1 c2))]) + +(define deltas (list (posn 0 1) (posn 0 -1) (posn 1 0) (posn -1 0))) + +(define input (fetch-aoc-input (find-session) 2023 17 #:cache #true)) + +(define grid + (for*/hash ([(row r) (in-indexed (in-list (string-split input "\n")))] + [(col c) (in-indexed (in-string row))]) + (values (posn r c) (~> col string string->number)))) + +(define goal-posn (~>> grid hash-keys (argmax (λ (p) (+ (posn-r p) (posn-c p)))))) + +(define (make-key s) + (cons (state-p s) (same-dir s))) + +(define (goal? n s) + (and (equal? goal-posn (state-p s)) + (>= (length (same-dir s)) n))) + +(define (same-dir s) + (define history (state-history s)) + (if (empty? history) + '() + (takef history (λ (n) (equal? n (car history)))))) + +(define (find-good-neighbors min-dist max-dist s) + (match-define (state p hl prev hist) s) + + (define (eliminate-bad-neighbors delta) + (define neighbor (add p delta)) + (cond + [(or (equal? neighbor prev) (not (hash-has-key? grid neighbor))) #false] + [else + (define same (same-dir s)) + (define l (length same)) + (cond + [(= max-dist l) (not (equal? delta (car same)))] + [(= l 0) #true] + [(< l min-dist) (equal? delta (car same))] + [else #t])])) + + (define (make-state delta) + (define neighbor (add p delta)) + (define new-loss (+ hl (hash-ref grid neighbor))) + (state neighbor new-loss p (cons delta hist))) + + (~>> deltas (filter eliminate-bad-neighbors) (map make-state))) + +(define (find-path neighbor-fn goal-fn) + (define seen (mutable-set)) + (define queue (make-heap (λ (a b) (<= (state-heat-lost a) (state-heat-lost b))))) + (heap-add! queue (state (posn 0 0) 0 'none '())) + + (let bfs () + (define s (heap-min queue)) + (heap-remove-min! queue) + (define key (make-key s)) + (cond + [(set-member? seen key) (bfs)] + [else + (set-add! seen key) + (define neighbors (neighbor-fn s)) + (define final (findf goal-fn neighbors)) + (if final + (state-heat-lost final) + (begin + (for ([n (in-list neighbors)]) + (heap-add! queue n)) + (bfs)))]))) + +;; part 1 +(find-path (curry find-good-neighbors 0 3) (curry goal? 1)) + +;; part 2 +(find-path (curry find-good-neighbors 4 10) (curry goal? 4)) \ No newline at end of file diff --git a/racket/aoc2023/day-18/day-18.rkt b/racket/aoc2023/day-18/day-18.rkt new file mode 100644 index 0000000..b589e41 --- /dev/null +++ b/racket/aoc2023/day-18/day-18.rkt @@ -0,0 +1,48 @@ +#lang racket +(require advent-of-code + threading) + +(struct coord (x y)) + +(define input (~> (fetch-aoc-input (find-session) 2023 18 #:cache #true))) + +(define (go-to-next-coord c dir dist) + (match-define (coord x y) c) + (match dir + [(or "R" "0") (coord (+ x dist) y)] + [(or "D" "1") (coord x (- y dist))] + [(or "L" "2") (coord (- x dist) y)] + [(or "U" "3") (coord x (+ y dist))])) + +(define/match (triangle-area _coord1 _coord2) + [((coord x1 y1) (coord x2 y2)) (/ (- (* x1 y2) (* x2 y1)) 2)]) + +(define (find-area-using parser) + (for/fold ([area 0] + [perimeter 0] + [current-coord (coord 0 0)] + #:result (+ 1 (abs area) (/ perimeter 2))) + ([dig (in-list (string-split input "\n"))]) + (define-values (dir dist) (parser dig)) + (define next-coord (go-to-next-coord current-coord dir dist)) + (values (+ area (triangle-area current-coord next-coord)) + (+ perimeter dist) next-coord))) + +;; part 1 +(define (parse-front dig) + (match-define (regexp #rx"(.) (.*) \\((.*)\\)" + (list _ dir (app string->number dist) _hex)) + dig) + (values dir dist)) + +(find-area-using parse-front) + +;; part 2 + +(define (parse-hex dig) + (match-define (regexp #rx".*\\(#(.....)(.)\\)" + (list _ (app (curryr string->number 16) dist) dir)) + dig) + (values dir dist)) + +(find-area-using parse-hex) diff --git a/racket/aoc2023/day-19/day-19.rkt b/racket/aoc2023/day-19/day-19.rkt new file mode 100644 index 0000000..f7561f6 --- /dev/null +++ b/racket/aoc2023/day-19/day-19.rkt @@ -0,0 +1,134 @@ +#lang racket + +(require advent-of-code + threading + data/applicative + data/monad + megaparsack + megaparsack/text + racket/struct) + +(struct part (x m a s)) +(struct rule (rating comparison threshold action)) +(struct just (action)) +(struct interval (from to)) + +(match-define (list raw-workflows raw-parts) + (~> (fetch-aoc-input (find-session) 2023 19 #:cache #true) + (string-split "\n\n") + (map (curryr string-split "\n") _))) + +(define/match (to-getter _) + [(#\x) part-x] + [(#\m) part-m] + [(#\a) part-a] + [(#\s) part-s]) + +(define/match (to-comp _) + [(#\>) >] + [(#\<) <]) + +(define/match (to-action _) + [('(#\R)) 'reject] + [('(#\A)) 'accept] + [(name) (apply string name)]) + +(define rule/p + (do (or/p + (try/p (do [rating <- (char-in/p "xmas")] + [comparison <- (char-in/p "<>")] + [threshold <- integer/p] + (char/p #\:) + [action <- (many+/p letter/p)] + (pure (rule (to-getter rating) + (to-comp comparison) + threshold + (to-action action))))) + (do [name <- (many+/p letter/p)] (pure (just (to-action name))))))) +(define rules/p + (do [name <- (many+/p letter/p)] + (char/p #\{) + [rules <- (many+/p rule/p #:sep (char/p #\,))] + (char/p #\}) + (pure (cons (list->string name) rules)))) + +(define rating/p (do letter/p (char/p #\=) integer/p)) +(define parts/p + (do (string/p "{") + [ratings <- (many/p rating/p #:sep (char/p #\,) #:min 4 #:max 4)] + (string/p "}") + (pure (apply part ratings)))) + +(define workflows + (~>> raw-workflows + (map (λ~>> (parse-string rules/p) parse-result!)) + make-immutable-hash)) +(define parts (map (λ~>> (parse-string parts/p) parse-result!) raw-parts)) + +;; part 1 + +(define (evaluate-workflow p [workflow-name "in"]) + (define rules (hash-ref workflows workflow-name)) + (match (evaluate-rules p rules) + ['accept (~> p struct->list (apply + _))] + ['reject 0] + [name (evaluate-workflow p name)])) + +(define (evaluate-rules p rules) + (match rules + [(list* (just action) _) action] + [(list* (rule rating comparison threshold action) _) + #:when (comparison (rating p) threshold) + action] + [(list* _ tail) (evaluate-rules p tail)])) + +(for/sum ([p (in-list parts)]) (evaluate-workflow p)) + +;; part 2 + +(define (part-update-range pr rating i) + (match rating + [(== part-x) (struct-copy part pr (x i))] + [(== part-m) (struct-copy part pr (m i))] + [(== part-a) (struct-copy part pr (a i))] + [(== part-s) (struct-copy part pr (s i))])) + +(define (evaluate-workflow-on-range pr [workflow-name "in"]) + (define rules (hash-ref workflows workflow-name)) + (evaluate-rules-on-range pr rules)) + +(define (evaluate-rules-on-range pr rules) + (match rules + [(list* (just 'accept) _) + (~> pr struct->list + (map (λ (i) (add1 (- (interval-to i) (interval-from i)))) _) + (apply * _))] + [(list* (just 'reject) _) 0] + [(list* (just name) _) (evaluate-workflow-on-range pr name)] + [(list* (rule rating (== <) threshold action) tail) + (match-define (interval i-min i-max) (rating pr)) + (split-range pr + rating + (interval i-min (sub1 threshold)) + action + (interval threshold i-max) + tail)] + [(list* (rule rating (== >) threshold action) tail) + (match-define (interval i-min i-max) (rating pr)) + (split-range pr + rating + (interval (add1 threshold) i-max) + action + (interval i-min threshold) + tail)])) + +(define (split-range pr rating keep action pass rules) + (+ (evaluate-rules-on-range (part-update-range pr rating keep) + (list (just action))) + (evaluate-rules-on-range (part-update-range pr rating pass) + rules))) + +(define start-interval (interval 1 4000)) + +(evaluate-workflow-on-range + (part start-interval start-interval start-interval start-interval)) diff --git a/racket/aoc2023/day-20/day-20.rkt b/racket/aoc2023/day-20/day-20.rkt new file mode 100644 index 0000000..2e3852d --- /dev/null +++ b/racket/aoc2023/day-20/day-20.rkt @@ -0,0 +1,144 @@ +#lang racket + +(require advent-of-code + threading + data/applicative + data/monad + megaparsack + megaparsack/text) + +(struct broadcaster ()) +(struct flipflop (state received)) +(struct conjunction (recieved)) +(struct cable (type dests)) +(struct nothing ()) + +(define charlist->symbol (λ~>> (apply string) string->symbol)) + +(define input (fetch-aoc-input (find-session) 2023 20 #:cache true)) + +(define module/p + (do (or/p (do (char/p #\%) + [name <- (many+/p letter/p)] + (pure (cons (charlist->symbol name) (flipflop 'off '())))) + (do (char/p #\&) + [name <- (many+/p letter/p)] + (pure (cons (charlist->symbol name) (conjunction (hash))))) + (do [name <- (many+/p letter/p)] + (pure (cons (charlist->symbol name) (broadcaster))))))) + +(define cable/p + (do [mod <- module/p] + (string/p " -> ") + [names <- (many/p (many+/p letter/p) #:sep (string/p ", "))] + (pure (cable mod (map charlist->symbol names))))) + +(define cables (~> input (string-split "\n") (map (λ~>> (parse-string cable/p) parse-result!) _))) + +(define destinations + (for/hash ([cable (in-list cables)]) + (values (car (cable-type cable)) (cable-dests cable)))) + +(define (set-conjunction-initial-state c) + (cond + [(conjunction? (cdr c)) + (~>> destinations + hash-keys + (filter (λ (k) (member (car c) (hash-ref destinations k)))) + (map (λ (k) (cons k 'low))) + (make-immutable-hash) + conjunction + (cons (car c)))] + [else c])) + +(define (make-initial-conditions-hash cables) + (~>> cables + (map cable-type) + (map set-conjunction-initial-state) + make-immutable-hash)) + +(define (receive mod from tone) + (match mod + [(flipflop state queue) (flipflop state (append queue (list tone)))] + [(conjunction received) (conjunction (hash-set received from tone))] + [(nothing) (nothing)])) + +; needed for part 2 +(define to-rx '(rk cd zf qx)) +(define sentry-tones (make-hash (for/list ([node to-rx]) (cons node 0)))) + +(define (press-button-once current-state this-round) + (for/fold ([queue '(broadcaster)] + [all-cables-state current-state] + [high 0] + [low 0] + #:result (values all-cables-state high low)) + ([_i (in-naturals)] #:break (empty? queue)) + (match-define (list* hd tl) queue) + (define to (hash-ref destinations hd (nothing))) + (match (hash-ref all-cables-state hd) + [(broadcaster) + (define state* + (foldl (λ (r acc) (hash-update acc r (λ~> (receive hd 'low)) (nothing))) + all-cables-state + to)) + (values (hash-ref destinations 'broadcaster) state* high (+ (length to) (add1 low)))] + [(flipflop 'off (list* 'low q)) + (define state* + (~> all-cables-state + (foldl (λ (r acc) + (when (member r to-rx) + (println (~a r " received high tone at " this-round))) + (hash-update acc r (λ~> (receive hd 'high)) (nothing))) + _ + to) + (hash-set _ hd (flipflop 'on q)))) + (values (append tl to) state* (+ (length to) high) low)] + [(flipflop 'on (list* 'low q)) + (define state* + (~> all-cables-state + (foldl (λ (r acc) (hash-update acc r (λ~> (receive hd 'low)) (nothing))) _ to) + (hash-set _ hd (flipflop 'off q)))) + (values (append tl to) state* high (+ (length to) low))] + [(flipflop on-or-off (list* 'high q)) + (define state* (~> all-cables-state (hash-set _ hd (flipflop on-or-off q)))) + (values tl state* high low)] + [(conjunction received) + #:when (or (empty? (hash-values received)) (member 'low (hash-values received))) + + (when (member hd to-rx) + (hash-set! sentry-tones hd this-round)) + (define state* + (foldl (λ (r acc) + (hash-update acc r (λ~> (receive hd 'high)) (nothing))) + all-cables-state + to)) + (values (append to tl) state* (+ (length to) high) low)] + [(conjunction _) + (define state* + (foldl (λ (r acc) (hash-update acc r (λ~> (receive hd 'low)) (nothing))) + all-cables-state + to)) + (values (append tl to) state* high (+ (length to) low))] + [(nothing) (values tl all-cables-state high low)]))) + +;; part 1 +(for/fold ([starting-state (make-initial-conditions-hash cables)] + [high 0] + [low 0] + #:result (* high low)) + ([i (in-range 1000)]) + (define-values (next-state this-high this-low) (press-button-once starting-state i)) + (values next-state (+ high this-high) (+ low this-low))) + +;; part 2 +;; rx receives a tone from gh, which receives four tones itself +;; those tones arrive on regular synced cycles so it's just the LCM of those cycle lengths +;; and since those cycle lengths are prime, it reduces to the product of the lengths +;; this is a really hacky mutable solution, I'm sure there's better ways of flagging these cycles + +(for/fold ([starting-state (make-initial-conditions-hash cables)] + #:result (apply * (hash-values sentry-tones))) + ([i (in-range 1 5000)]) + (define-values (next-state _high _low) (press-button-once starting-state i)) + (values next-state)) \ No newline at end of file diff --git a/racket/aoc2023/day-21/day-21.rkt b/racket/aoc2023/day-21/day-21.rkt new file mode 100644 index 0000000..b5478eb --- /dev/null +++ b/racket/aoc2023/day-21/day-21.rkt @@ -0,0 +1,69 @@ +#lang racket + +(require advent-of-code + threading + simple-polynomial + racket/hash) + +(define input (fetch-aoc-input (find-session) 2023 21 #:cache #true)) + +(define initial-garden + (~> (for*/list ([(row r) (in-indexed (string-split input "\n"))] + [(col c) (in-indexed row)] + #:unless (equal? col #\#)) + (cons (cons r c) (if (equal? col #\S) 'on 'off))) + make-hash)) + +(define (neighbors p) + (match-define (cons r c) p) + (list (cons (add1 r) c) (cons (sub1 r) c) (cons r (add1 c)) (cons r (sub1 c)))) + +(define (make-n-steps garden n) + (define g (hash-copy garden)) + (define (make-one-step) + (define update (make-hash)) + (for ([(cons state) (in-hash g)] #:when (equal? state 'on)) + (hash-set! update cons 'off) + (for ([neighbor (in-list (neighbors cons))] #:when (hash-has-key? g neighbor)) + (hash-set! update neighbor 'on))) + (hash-union! g update #:combine/key (λ (_k _v v) v))) + (for/fold ([_ void] + #:result (~>> g hash-values (count (curry equal? 'on)))) + ([i (in-range n)]) + (displayln i) + (make-one-step))) + +;; part 1 +(make-n-steps initial-garden 64) + +;; part 2 +;; the growth of the steps pattern is regular and quadratic +;; the rock pattern has aisles in it that allow the steps pattern to expand freely +;; such that it will cross from one side to another in X steps +;; where X is the height/width of the repeated section + +(define grid-size (~> input (string-split "\n") length)) +(define half-size (/ (sub1 grid-size) 2)) + +(define expanded-garden + (~> (for*/list (#:do [(define rows (string-split input "\n"))] + #:do [(define height (length rows))] + [(row r) (in-indexed rows)] + #:do [(define width (string-length row))] + [(col c) (in-indexed row)] + #:unless (equal? col #\#) + [n (in-inclusive-range -3 3)] + [m (in-inclusive-range -3 3)]) + + (cons (cons (+ r (* n height)) (+ c (* m width))) + (if (and (= n m 0) (equal? col #\S)) 'on 'off))) + make-hash)) + +(define fitting-points + (for/list ([n (in-range 3)] #:do [(define size (+ half-size (* n grid-size)))]) + (cons n (make-n-steps expanded-garden size)))) + +(define end-cycle 26501365) +(define x (/ (- end-cycle half-size) grid-size)) + +((points->polynomial fitting-points) x) \ No newline at end of file diff --git a/racket/aoc2023/day-22/day-22.rkt b/racket/aoc2023/day-22/day-22.rkt new file mode 100644 index 0000000..53668c0 --- /dev/null +++ b/racket/aoc2023/day-22/day-22.rkt @@ -0,0 +1,109 @@ +#lang racket + +(require advent-of-code + threading + data/applicative + data/monad + megaparsack + megaparsack/text + racket/hash) + +(struct posn (x y z)) +(struct block (n from to)) + +(define input (fetch-aoc-input (find-session) 2023 22 #:cache #true)) + +(define coordinate/p + (do [coords <- (many/p integer/p #:sep (char/p #\,) #:min 3 #:max 3)] + (pure (apply posn coords)))) + +(define block/p + (do [from <- coordinate/p] + (char/p #\~) + [to <- coordinate/p] + (pure (cons from to)))) + +(define starting-blocks + (~> (for/list ([line (in-list (string-split input "\n"))] + [n (in-naturals)]) + (match-define (cons from to) (parse-result! (parse-string block/p line))) + (block n from to)) + (sort < #:key (λ~> block-from posn-z)))) + +(define (all-in-cross-section-at-level b z) + (match-define (block _ (posn x1 y1 _) (posn x2 y2 _)) b) + (for*/list ([x (in-inclusive-range x1 x2)] + [y (in-inclusive-range y1 y2)]) + (posn x y z))) + +(define (place-block-at-level b h dz) + (match-define (block n (posn x1 y1 z1) (posn x2 y2 z2)) b) + (define now-occupied + (for*/hash ([x (in-inclusive-range x1 x2)] + [y (in-inclusive-range y1 y2)] + [z (in-inclusive-range dz (+ dz (- z2 z1)))]) + (values (posn x y z) n))) + (hash-union h now-occupied)) + +(define (find-lowest-level b h [z (~> b block-from posn-z)]) + (cond + [(= z 0) + (place-block-at-level b h 1)] + [(findf (curry hash-has-key? h) (all-in-cross-section-at-level b z)) + (place-block-at-level b h (add1 z))] + [else + (find-lowest-level b h (sub1 z))])) + +(define blocks-in-space (foldl find-lowest-level (hash) starting-blocks)) +(define block-positions + (for/fold ([placed-blocks (hash)]) + ([(p n) (in-hash blocks-in-space)]) + (hash-update placed-blocks n (curryr set-add p) (set)))) + +(define (down-one p) + (match p + [(posn x y z) (posn x y (sub1 z))])) + +(define supporting-blocks + (for/hash ([(n-id n-posns) (in-hash block-positions)]) + (values n-id + (for*/set ([(m-id m-posns) (in-hash block-positions)] + #:unless (= n-id m-id) + [m-posn (in-set m-posns)] + #:when (set-member? n-posns (down-one m-posn))) + m-id)))) + +(define supported-by-blocks + (for/hash ([n-id (in-hash-keys supporting-blocks)]) + (define supporters + (~> (for*/set + ([(m-id m-supporting) (in-hash supporting-blocks)] + #:unless (= n-id m-id) + #:when (set-member? m-supporting n-id)) + m-id) + ((λ (s) (if (set-empty? s) (set 'ground) s))))) + (values n-id supporters))) + +;; part 1 +(define vulnerable-blocks + (for/list ([n-id (in-range (length starting-blocks))] + #:when (for/or ([m-supported-by (in-hash-values supported-by-blocks)]) + (set-empty? (set-remove m-supported-by n-id)))) + n-id)) +(- (length starting-blocks) (length vulnerable-blocks)) + +;; part 2 +(for/sum ([n (in-list vulnerable-blocks)]) + (for/fold ([fallen (set n)] + [bricks (set n)] + #:result (~> fallen set-count sub1)) + ([_ (in-naturals)]) + #:break (set-empty? bricks) + (define bricks-above + (for*/set + ([brick (in-set bricks)] + [supporting (in-set (hash-ref supporting-blocks brick))] + #:when (for/and ([supports (in-set (hash-ref supported-by-blocks supporting))]) + (set-member? fallen supports))) + supporting)) + (values (set-union fallen bricks-above) bricks-above))) \ No newline at end of file diff --git a/racket/aoc2023/day-23/day-23.rkt b/racket/aoc2023/day-23/day-23.rkt new file mode 100644 index 0000000..c048013 --- /dev/null +++ b/racket/aoc2023/day-23/day-23.rkt @@ -0,0 +1,89 @@ +#lang racket + +(require advent-of-code + threading + graph) + +(define input (fetch-aoc-input (find-session) 2023 23 #:cache #true)) +(define trails + (for*/hash ([(row r) (in-indexed (string-split input "\n"))] + [(col c) (in-indexed row)] + #:when (not (equal? col #\#))) + ; for now, we don't actually need to detect paths and slopes, just not-rocks + ; in part 1, all forks in the road go right or down, so we can just infer the path + ; direction from the shape of the junction and form a network of junctions from there + ; in part 2, the slopes are removed anyway + (values (cons (add1 r) (add1 c)) col))) + +(define max-row (~> input (string-split "\n") length)) + +(define start (findf (λ (p) (= (car p) 1)) (hash-keys trails))) +(define end (findf (λ (p) (= (car p) max-row)) (hash-keys trails))) + +(define (get-neighbors posn type) + (match-define (cons r c) posn) + (match type + ['junction + (~> (set (cons (add1 r) c) (cons r (add1 c))))] + [_ + (~> (list (cons (add1 r) c) (cons (sub1 r) c) (cons r (add1 c)) (cons r (sub1 c))) + (filter (curry hash-has-key? trails) _) + list->set)])) + +(define junction-points + (for/set ([(k v) (in-hash trails)] + #:when (not (= (set-count (get-neighbors k v)) 2))) + k)) + +(define trails-with-junctions + (for/hash ([k (in-hash-keys trails)]) + (cond + [(set-member? junction-points k) (values k 'junction)] + [else (values k 'trail)]))) + +(define (walk-to-next-junction start current [length 1] [seen (set start)]) + (define next (~> current + (get-neighbors _ 'trail) + (set-subtract seen) set-first)) + (cond + [(equal? (hash-ref trails-with-junctions next) 'junction) + (list (- (add1 length)) start next)] ; weird format is due to graph library + [else + (walk-to-next-junction start next (add1 length) (set-add seen current))])) + +(define routes-to-junctions + (for*/list ([j (in-set junction-points)] + [neighbor (in-set (get-neighbors j 'junction))] + #:when (hash-has-key? trails neighbor)) + (walk-to-next-junction j neighbor))) + +;; part 1 -- using graph library for Bellman-Ford on negative weighted graph +;; Bellman-Ford finds the shortest path, but negating all the path weights +;; will give us the longest path instead +;; +;; unlike Dijkstra which can't handle negative path lengths, Bellman-Ford +;; works as long as the graph is currently directed and acyclic +(define slippery-trail (weighted-graph/directed routes-to-junctions)) +(match-define-values (distances _) (bellman-ford slippery-trail start)) +(- (hash-ref distances end)) + +;; part 2 -- rolling my own DFS that can reject seen junctions and dead ends +(define routes-to-junctions-with-traction + (for/fold ([trails (hash)]) ([route (in-list routes-to-junctions)]) + (match-define (list (app - weight) from to) route) + (~> trails + (hash-update _ from (curry cons (cons to weight)) '()) + (hash-update _ to (curry cons (cons from weight)) '())))) + +(define (dfs g from to [acc 0] [seen (set from)]) + (cond + [(equal? from to) acc] + [else + (define choices (filter (λ (path) (not (set-member? seen (car path)))) (hash-ref g from))) + (if (empty? choices) 0 ; long dead-ends don't count + (for/fold ([best acc]) + ([path (in-list choices)] + #:do [(match-define (cons next dist) path)]) + (max best (dfs g next to (+ acc dist) (set-add seen next)))))])) + +(dfs routes-to-junctions-with-traction start end) \ No newline at end of file diff --git a/racket/aoc2023/day-24/day-24a.rkt b/racket/aoc2023/day-24/day-24a.rkt new file mode 100644 index 0000000..31f526d --- /dev/null +++ b/racket/aoc2023/day-24/day-24a.rkt @@ -0,0 +1,51 @@ +#lang rosette + +(require advent-of-code + threading) + +(struct hail (posn vel) #:transparent) +(struct posn (x y z) #:transparent) +(struct vel (x y z) #:transparent) + +(define input (fetch-aoc-input (find-session) 2023 24 #:cache #true)) + +(define LOWER-BOUND 200000000000000) +(define UPPER-BOUND 400000000000000) + +(define (->struct f str) + (~> str (string-split _ ",") (map (λ~> string-trim string->number) _) (apply f _))) + +(define (parse-hail-record str) + (match-define (list p v) (string-split str " @ ")) + (hail (->struct posn p) + (->struct vel v))) + +(define hail-paths + (for/list ([hail (in-list (string-split input "\n"))]) + (parse-hail-record hail))) + +;; part 1 +(define (valid-intersection? h1 h2) + (match-define (hail (posn x1 y1 _) (vel vx1 vy1 _)) h1) + (match-define (hail (posn x2 y2 _) (vel vx2 vy2 _)) h2) + (cond + [(= (* vy1 vx2) (* vx1 vy2)) #f] + [else + (define t1 (/ (- (* vy2 (- x1 x2)) (* vx2 (- y1 y2))) + (- (* vy1 vx2) (* vx1 vy2)))) + (define t2 (/ (- (* vy1 (- x2 x1)) (* vx1 (- y2 y1))) + (- (* vy2 vx1) (* vx2 vy1)))) + + (define x (+ x1 (* t1 vx1))) + (define y (+ y1 (* t1 vy1))) + + (and (<= LOWER-BOUND x UPPER-BOUND) + (<= LOWER-BOUND y UPPER-BOUND) + (<= 0 t1) + (<= 0 t2))])) + +(for/sum ([(trial-paths) (in-combinations hail-paths 2)] ; + #:when (apply valid-intersection? trial-paths)) + 1) + +;; part 2 - see day-24b.rkt diff --git a/racket/aoc2023/day-24/day-24b.rkt b/racket/aoc2023/day-24/day-24b.rkt new file mode 100644 index 0000000..b106b30 --- /dev/null +++ b/racket/aoc2023/day-24/day-24b.rkt @@ -0,0 +1,37 @@ +#lang rosette + +(require advent-of-code + threading) + +(struct hail (posn vel)) +(struct posn (x y z)) +(struct vel (x y z)) + +(define input (fetch-aoc-input (find-session) 2023 24 #:cache #true)) + +(define (->struct f str) + (~> str (string-split _ ",") (map (λ~> string-trim string->number) _) (apply f _))) + +(define (parse-hail-record str) + (match-define (list p v) (string-split str " @ ")) + (hail (->struct posn p) (->struct vel v))) + +(define hail-paths + (for/list ([hail (in-list (string-split input "\n"))] ; + [_ (in-range 3)]) + (parse-hail-record hail))) + +;; part 1 - see day-24a.rkt +;; part 2 + +(define-symbolic px py pz vx vy vz integer?) + +(define sol + (solve ; + (for ([path (in-list hail-paths)]) + (define-symbolic* t integer?) + (assert (= (+ px (* vx t)) (+ (~> path hail-posn posn-x) (* (~> path hail-vel vel-x) t)))) + (assert (= (+ py (* vy t)) (+ (~> path hail-posn posn-y) (* (~> path hail-vel vel-y) t)))) + (assert (= (+ pz (* vz t)) (+ (~> path hail-posn posn-z) (* (~> path hail-vel vel-z) t))))))) + +(evaluate (+ px py pz) sol) diff --git a/racket/aoc2023/day-25/day-25.rkt b/racket/aoc2023/day-25/day-25.rkt new file mode 100644 index 0000000..aa32e43 --- /dev/null +++ b/racket/aoc2023/day-25/day-25.rkt @@ -0,0 +1,43 @@ +#lang racket + +(require advent-of-code + threading + graph) + +(define input + (~> (fetch-aoc-input (find-session) 2023 25 #:cache #true) + (string-split "\n") + (map (curryr string-split ": ") _))) + +(define all-wires + (for*/list ([wire-diagram (in-list input)] [devices (in-list (string-split (second wire-diagram)))]) + (list (car wire-diagram) devices))) + +;; instead of trying to solve the minimum cut problem, I generated the graph and +;; rendered it in graphviz: + +; (define out (open-output-file "graphviz")) +; (~> all-wires +; unweighted-graph/undirected +; graphviz +; (display out)) +; (close-output-port out) + +;; the bottleneck is very obvious on the graph -- +;; there's two large clusters of nodes, connected by just three edges +;; +;; from the graphviz output, the three critical wires are +;; cpq-hlx +;; hqp-spk +;; chr-zlx + +(define remove-these-three '(("cpq" "hlx") ("hqp" "spk") ("chr" "zlx"))) +(define cut-wires + (for/list ([wire (in-list all-wires)] #:unless (member (sort wire string cut-wires + unweighted-graph/undirected + scc + (map length _) + (apply * _)) \ No newline at end of file diff --git a/racket/leetcode/lc-1018-binary-prefix.rkt b/racket/leetcode/lc-1018-binary-prefix.rkt new file mode 100644 index 0000000..fa82681 --- /dev/null +++ b/racket/leetcode/lc-1018-binary-prefix.rkt @@ -0,0 +1,11 @@ +#lang racket +(define/contract (prefixes-div-by5 A) + (-> (listof exact-integer?) (listof boolean?)) + (define ns (make-vector (length A) #false)) + (for/fold ([acc 0]) + ([b (in-list A)] + [i (in-naturals)]) + (let ([test-val (remainder (+ (* 2. acc) b) 5)]) + (when (= 0 test-val) (vector-set! ns i #true)) + test-val)) + (vector->list ns)) \ No newline at end of file diff --git a/racket/leetcode/lc-1037-boomerang.rkt b/racket/leetcode/lc-1037-boomerang.rkt new file mode 100644 index 0000000..fd95695 --- /dev/null +++ b/racket/leetcode/lc-1037-boomerang.rkt @@ -0,0 +1,21 @@ +#lang racket +(define/contract (is-boomerang points) + (-> (listof (listof exact-integer?)) boolean?) + (match points + [(list-no-order a b c) #:when (equal? a b) #false] ; Are any two points the same? + [(list (list x _) (list x _) (list x _)) #false] ; Are they on a horizontal line? + [(list (list _ y) (list _ y) (list _ y)) #false] ; Are they on a vertical line? + [(list-no-order (list x1 _) (list x2 _) (list x3 _)) ; Are two points on a horizontal line, + #:when (and (= x1 x2) ; but the third point isn't? + (not (= x1 x3))) #true] + [(list-no-order (list _ y1) (list _ y2) (list _ y3)) ; Are two points on a vertical line, + #:when (and (= y1 y2) ; but the third point isn't? + (not (= y1 y3))) #true] + [(list (list x1 y1) (list x2 y2) (list x3 y3)) ; If none of the special cases apply, + (let ([m (/ (- y2 y1) (- x2 x1))]) ; calculate the slope between two points + (not (= y3 (+ y1 (* m (- x3 x1))))))])) ; and see if the line passes through the third + +(is-boomerang '((1 1) (2 3) (3 2))) +(is-boomerang '((1 1) (2 2) (3 3))) +(is-boomerang '((0 0) (0 2) (2 1))) +(is-boomerang '((0 0) (1 1) (1 1))) \ No newline at end of file diff --git a/racket/leetcode/lc-1185-day-of-week.rkt b/racket/leetcode/lc-1185-day-of-week.rkt new file mode 100644 index 0000000..c90a626 --- /dev/null +++ b/racket/leetcode/lc-1185-day-of-week.rkt @@ -0,0 +1,18 @@ +#lang racket +(require racket/date) + +(define day-names + (for/hash ([day-number (in-range 0 7)] + [day-name (in-list '("Sunday" + "Monday" + "Tuesday" + "Thursday" + "Friday" + "Saturday"))]) + (values day-number day-name))) + +(define/contract (day-of-the-week day month year) + (-> exact-integer? exact-integer? exact-integer? string?) + (hash-ref day-names (date-week-day + (seconds->date + (find-seconds 0 0 0 day month year))))) diff --git a/racket/leetcode/lc-1207-unique-occurences.rkt b/racket/leetcode/lc-1207-unique-occurences.rkt new file mode 100644 index 0000000..1b4d107 --- /dev/null +++ b/racket/leetcode/lc-1207-unique-occurences.rkt @@ -0,0 +1,10 @@ +#lang racket +(define/contract (unique-occurrences arr) + (-> (listof exact-integer?) boolean?) + (define occurrences (make-hash)) + (for ([n (in-list arr)]) + (hash-update! occurrences n add1 1)) + (equal? (hash-values occurrences) + (remove-duplicates (hash-values occurrences)))) + +(unique-occurrences '(1 2 2 1 1 3)) \ No newline at end of file diff --git a/racket/leetcode/lc-1221-split-a-string-balanced.rkt b/racket/leetcode/lc-1221-split-a-string-balanced.rkt new file mode 100644 index 0000000..4c75770 --- /dev/null +++ b/racket/leetcode/lc-1221-split-a-string-balanced.rkt @@ -0,0 +1,19 @@ +#lang racket +(require rackunit) + +(define/contract (balanced-string-split s) + (-> string? exact-integer?) + (for/fold ([acc 0] + [count 0] + #:result count) + ([c (string->list s)]) + (let* ([increment (case c + [(#\R) 1] + [(#\L) -1])] + [new-acc (+ increment acc)] + [new-count (case new-acc + [(0) (add1 count)] + [else count])]) + (values new-acc new-count)))) + +(check-eq? (balanced-string-split "RLRRLLRLRL") 4) \ No newline at end of file diff --git a/racket/leetcode/lc-125-valid-palindrome.rkt b/racket/leetcode/lc-125-valid-palindrome.rkt new file mode 100644 index 0000000..ed91d08 --- /dev/null +++ b/racket/leetcode/lc-125-valid-palindrome.rkt @@ -0,0 +1,12 @@ +#lang racket + +(define/contract (is-palindrome s) + (-> string? boolean?) + (define clean-string + (string-downcase (string-replace s #rx"[^A-Za-z0-9]" ""))) + (string-prefix? (apply string-append (map string (reverse (string->list clean-string)))) + (substring clean-string + 0 + (ceiling (/ (string-length clean-string) 2))))) + +(is-palindrome "A man, a plan, a canal: Panama") \ No newline at end of file diff --git a/racket/leetcode/lc-1295-even-number-of-digits.rkt b/racket/leetcode/lc-1295-even-number-of-digits.rkt new file mode 100644 index 0000000..9e88454 --- /dev/null +++ b/racket/leetcode/lc-1295-even-number-of-digits.rkt @@ -0,0 +1,4 @@ +#lang racket +(define/contract (find-numbers nums) + (-> (listof exact-integer?) exact-integer?) + (count (λ (n) (odd? (order-of-magnitude n))) nums)) \ No newline at end of file diff --git a/racket/leetcode/lc-1299-replace-with-greatest-to-right.rkt b/racket/leetcode/lc-1299-replace-with-greatest-to-right.rkt new file mode 100644 index 0000000..34d3eae --- /dev/null +++ b/racket/leetcode/lc-1299-replace-with-greatest-to-right.rkt @@ -0,0 +1,8 @@ +#lang racket +(define/contract (replace-elements arr) + (-> (listof exact-integer?) (listof exact-integer?)) + (cond [(= 1 (length arr)) '(-1)] + [else (cons (apply max (cdr arr)) + (replace-elements (cdr arr)))])) + +(replace-elements '(17 18 5 4 6 1)) \ No newline at end of file diff --git a/racket/leetcode/lc-1304-find-n-unique-integers.rkt b/racket/leetcode/lc-1304-find-n-unique-integers.rkt new file mode 100644 index 0000000..9b810a0 --- /dev/null +++ b/racket/leetcode/lc-1304-find-n-unique-integers.rkt @@ -0,0 +1,5 @@ +#lang racket +(define/contract (sum-zero n) + (-> exact-integer? (listof exact-integer?)) + (cond [(even? n) (remove 0 (range (/ n -2) (add1 (/ n 2))))] + [(odd? n) (range (/ (- n 1) -2) (add1 (/ (- n 1) 2)))])) \ No newline at end of file diff --git a/racket/leetcode/lc-1436-destination-city.rkt b/racket/leetcode/lc-1436-destination-city.rkt new file mode 100644 index 0000000..ce82f08 --- /dev/null +++ b/racket/leetcode/lc-1436-destination-city.rkt @@ -0,0 +1,14 @@ +#lang racket +(define/contract (dest-city paths) + (-> (listof (listof string?)) string?) + (define city-pairs (make-hash paths)) + (define (go-to-next-city origin) + (let ([destination (hash-ref city-pairs origin #false)]) + (if destination + (go-to-next-city (car destination)) + origin))) + (go-to-next-city (caar paths))) + +(dest-city '(("London" "New York") + ("New York" "Lima") + ("Lima" "Sao Paolo"))) \ No newline at end of file diff --git a/racket/leetcode/lc-1450-students-doing-homework.rkt b/racket/leetcode/lc-1450-students-doing-homework.rkt new file mode 100644 index 0000000..14ff079 --- /dev/null +++ b/racket/leetcode/lc-1450-students-doing-homework.rkt @@ -0,0 +1,12 @@ +#lang racket +(define/contract (busy-student start-time end-time query-time) + (-> (listof exact-integer?) (listof exact-integer?) exact-integer? exact-integer?) + (count (λ (start end) + (and (start . <= . query-time) + (query-time . <= . end))) start-time end-time)) + +(busy-student '(1 2 3) '(3 2 7) 4) +(busy-student '(4) '(4) 4) +(busy-student '(9 8 7 6 5 4 3 2 1) + '(10 10 10 10 10 10 10 10 10) + 5) \ No newline at end of file diff --git a/racket/leetcode/lc-1460-make-two-arrays-equal.rkt b/racket/leetcode/lc-1460-make-two-arrays-equal.rkt new file mode 100644 index 0000000..584ac97 --- /dev/null +++ b/racket/leetcode/lc-1460-make-two-arrays-equal.rkt @@ -0,0 +1,4 @@ +#lang racket +(define/contract (can-be-equal target arr) + (-> (listof exact-integer?) (listof exact-integer?) boolean?) + (equal? (sort target <) (sort arr <))) \ No newline at end of file diff --git a/racket/leetcode/lc-1496-path-crossing.rkt b/racket/leetcode/lc-1496-path-crossing.rkt new file mode 100644 index 0000000..9c1941d --- /dev/null +++ b/racket/leetcode/lc-1496-path-crossing.rkt @@ -0,0 +1,25 @@ +#lang racket +(define/contract (is-path-crossing path) + (-> string? boolean?) + (for/fold ([current-x 0] + [current-y 0] + [trail (set '(0 0))] + [check #false] + #:result check) + ([step (in-list (string->list path))] + #:break check) + (let*-values + ([(new-x new-y) + (case step + [(#\N) (values current-x (add1 current-y))] + [(#\S) (values current-x (sub1 current-y))] + [(#\E) (values (add1 current-x) current-y)] + [(#\W) (values (sub1 current-x) current-y)])] + [(new-trail-point) (list new-x new-y)]) + (cond [(set-member? trail new-trail-point) + (values void void void #true)] + [else + (values new-x + new-y + (set-add trail new-trail-point) + #false)])))) \ No newline at end of file diff --git a/racket/leetcode/lc-1700-students-unable-to-eat.rkt b/racket/leetcode/lc-1700-students-unable-to-eat.rkt new file mode 100644 index 0000000..75cc243 --- /dev/null +++ b/racket/leetcode/lc-1700-students-unable-to-eat.rkt @@ -0,0 +1,36 @@ +#lang racket +(define/contract (count-students students sandwiches) + (-> (listof exact-integer?) (listof exact-integer?) exact-integer?) + (for/fold ([sandwich-pile sandwiches] + [student-line students] + [remaining-students (length students)] + [break? #false] + #:result remaining-students) + ([i (in-naturals)] + #:break break?) + (cond [(and (empty? sandwich-pile) + (empty? student-line)) + (values void + void + remaining-students + #true)] + [(equal? (car sandwich-pile) + (car student-line)) + (values (cdr sandwich-pile) + (cdr student-line) + (sub1 remaining-students) + #false)] + [(and (not (equal? (list (car sandwich-pile)) + (remove-duplicates student-line))) + (= 1 (length (remove-duplicates student-line)))) + (values void + void + remaining-students + #true)] + [else + (values sandwich-pile + (append (cdr student-line) (list (car student-line))) + remaining-students + #false)]))) + +(count-students '(1 1 0 0) '(0 1 0 1)) diff --git a/racket/leetcode/lc-1812-chessboard-square.rkt b/racket/leetcode/lc-1812-chessboard-square.rkt new file mode 100644 index 0000000..206392c --- /dev/null +++ b/racket/leetcode/lc-1812-chessboard-square.rkt @@ -0,0 +1,7 @@ +#lang racket +(define/contract (square-is-white coordinates) + (-> string? boolean?) + (define file (first (string->list coordinates))) + (define rank (second (string->list coordinates))) + (or (and (odd? (char->integer file)) (even? (char->integer rank))) + (and (even? (char->integer file)) (odd? (char->integer rank))))) \ No newline at end of file diff --git a/racket/leetcode/lc-1844-replace-all-digits-with-characters.rkt b/racket/leetcode/lc-1844-replace-all-digits-with-characters.rkt new file mode 100644 index 0000000..96aba6e --- /dev/null +++ b/racket/leetcode/lc-1844-replace-all-digits-with-characters.rkt @@ -0,0 +1,18 @@ +#lang racket +(define/contract (replace-digits s) + (-> string? string?) + (define/contract (shift-letter c x) + (-> char? char? char?) + (integer->char (+ (string->number (string x)) (char->integer c)))) + (define letters (string->list (string-replace s #rx"[0-9]" ""))) + (define digits (string->list (string-replace s #rx"[a-z]" ""))) + (foldl (λ (c x acc) + (if (equal? x #\X) + (string-append acc (string c)) + (string-append acc (string c) (string (shift-letter c x))))) + "" + letters + (if (= (length digits) (length letters)) + digits + (append digits '(#\X))) + )) \ No newline at end of file diff --git a/racket/leetcode/lc-1854-max-pop-year.rkt b/racket/leetcode/lc-1854-max-pop-year.rkt new file mode 100644 index 0000000..75104f1 --- /dev/null +++ b/racket/leetcode/lc-1854-max-pop-year.rkt @@ -0,0 +1,17 @@ +#lang racket +(define/contract (maximum-population logs) + (-> (listof (listof exact-integer?)) exact-integer?) + ; make a hash table of every year encountered between the birth and death years + (define population (make-hash)) + ; for each person in the logs, + (for/list ([person (in-list logs)]) + ; for every year from birth to the year before death, + (for/list ([year (in-range (first person) (second person))]) + ; look up the year in the hash table and add 1 to its key, + ; or add the key and set its value to 1 if it doesn't exist yet + (hash-update! population year add1 1))) + ; convert the hash table to a list, + ; sort the list by year, + ; find the first element that maximizes the count, + ; and return the associated year + (car (argmax cdr (sort (hash->list population) < #:key car)))) \ No newline at end of file diff --git a/racket/leetcode/lc-2-add-two-numbers.rkt b/racket/leetcode/lc-2-add-two-numbers.rkt new file mode 100644 index 0000000..8062817 --- /dev/null +++ b/racket/leetcode/lc-2-add-two-numbers.rkt @@ -0,0 +1,35 @@ +#lang racket +; Definition for singly-linked list: + + +; val : integer? +; next : (or/c list-node? #f) +(struct list-node + (val next) #:mutable #:transparent) + +; constructor +(define (make-list-node val [next-node #f]) + (list-node val next-node)) + + +(define/contract (add-two-numbers l1 l2) + (-> (or/c list-node? #f) (or/c list-node? #f) (or/c list-node? #f)) + (define (process-list node [acc '()]) + (if (list-node-next node) + (process-list (list-node-next node) (cons (list-node-val node) acc)) + (cons (list-node-val node) acc))) + (define sum-of-lists (+ (string->number (apply ~a (process-list l1))) + (string->number (apply ~a (process-list l2))))) + (define sum-list-digits + (reverse + (map (λ (x) (string->number (string x))) + (string->list (number->string sum-of-lists))))) + (define (build-list l) + (if (empty? l) + #f + (make-list-node (car l) (build-list (cdr l))))) + (build-list sum-list-digits)) + +(define list1 (make-list-node 2 (make-list-node 4 (make-list-node 3)))) +(define list2 (make-list-node 5 (make-list-node 6 (make-list-node 4)))) +(add-two-numbers list1 list2) \ No newline at end of file diff --git a/racket/leetcode/lc-217-contains-duplicate.rkt b/racket/leetcode/lc-217-contains-duplicate.rkt new file mode 100644 index 0000000..ca8d193 --- /dev/null +++ b/racket/leetcode/lc-217-contains-duplicate.rkt @@ -0,0 +1,12 @@ +#lang racket +(define/contract (contains-duplicate nums) + (-> (listof exact-integer?) boolean?) + (define nums-hash (make-hash)) + (define (check-next-number nums) + (cond [(empty? nums) #false] + [(hash-ref nums-hash (car nums) #false) #true] + [else (hash-set! nums-hash (car nums) #true) + (check-next-number (cdr nums))])) + (check-next-number nums)) + +(contains-duplicate '(1 2 3)) \ No newline at end of file diff --git a/racket/leetcode/lc-228-summary-ranges.rkt b/racket/leetcode/lc-228-summary-ranges.rkt new file mode 100644 index 0000000..9140895 --- /dev/null +++ b/racket/leetcode/lc-228-summary-ranges.rkt @@ -0,0 +1,27 @@ +#lang racket +(define (summary-ranges nums) + (define range-pairs + (cond + [(empty? nums) '()] + [(empty? (cdr nums)) (list (cons (car nums) (car nums)))] + [else (for/fold ([ranges '()] + [open-pair (first nums)] + [prev-num (first nums)] + #:result (append ranges (list (cons open-pair prev-num)))) + ([i (cdr nums)]) + (cond [(= (add1 prev-num) i) + (values ranges + open-pair + i)] + [else + (values (append ranges (list (cons open-pair prev-num))) + i + i)]))])) + (for/list ([p (in-list range-pairs)]) + (cond [(= (car p) (cdr p)) (format "~a" (car p))] + [else (format "~a->~a" (car p) (cdr p))]))) + +(summary-ranges '(0 1 2 4 5 7)) +(summary-ranges '(0 2 3 4 6 8 9)) +(summary-ranges '()) +(summary-ranges '(0)) \ No newline at end of file diff --git a/racket/leetcode/lc-290-word-pattern.rkt b/racket/leetcode/lc-290-word-pattern.rkt new file mode 100644 index 0000000..77cdba0 --- /dev/null +++ b/racket/leetcode/lc-290-word-pattern.rkt @@ -0,0 +1,23 @@ +#lang racket +(define match-string "abba") +(define a "dog") +(define b "cat") +(define Σ "dog cat cat dog") + +(define/contract (word-pattern pattern s) + (-> string? string? boolean?) + (define pattern-list (map string (string->list pattern))) + (define s-list (string-split s)) + (define match-hash (make-hash)) + (if (= (length pattern-list) (length s-list)) + (for/and ([pattern-part pattern-list] + [s-part s-list]) + (cond [(and (not (hash-has-key? match-hash pattern-part)) + (member s-part (hash-values match-hash))) #f] + [(not (hash-has-key? match-hash pattern-part)) + (hash-set! match-hash pattern-part s-part) #t] + [(string=? (hash-ref match-hash pattern-part) s-part) #t] + [else #f])) + #f)) + +(word-pattern match-string Σ) \ No newline at end of file diff --git a/racket/leetcode/lc-345-reverse-vowels.rkt b/racket/leetcode/lc-345-reverse-vowels.rkt new file mode 100644 index 0000000..c05bf2d --- /dev/null +++ b/racket/leetcode/lc-345-reverse-vowels.rkt @@ -0,0 +1,9 @@ +#lang racket + +(define/contract (reverse-vowels s) + (-> string? string?) + (define vowels-only + (string-replace s #rx"[^aeiouAEIOU]" "")) + (define consonants-with-placeholders + (string-replace s #rx"[aeiouAEIOU]" "~a")) + (apply format consonants-with-placeholders (reverse (string->list vowels-only)))) \ No newline at end of file diff --git a/racket/leetcode/lc-349-intersection-of-2-arrays.rkt b/racket/leetcode/lc-349-intersection-of-2-arrays.rkt new file mode 100644 index 0000000..14d56ca --- /dev/null +++ b/racket/leetcode/lc-349-intersection-of-2-arrays.rkt @@ -0,0 +1,5 @@ +#lang racket + +(define/contract (intersection nums1 nums2) + (-> (listof exact-integer?) (listof exact-integer?) (listof exact-integer?)) + (set-intersect nums1 nums2)) \ No newline at end of file diff --git a/racket/leetcode/lc-36-valid-sudoku.rkt b/racket/leetcode/lc-36-valid-sudoku.rkt new file mode 100644 index 0000000..915b533 --- /dev/null +++ b/racket/leetcode/lc-36-valid-sudoku.rkt @@ -0,0 +1,33 @@ +#lang racket + +(define (pos board r c) + (list-ref (list-ref board r) c)) + +(define (scan-for-duplicates array) + (andmap (λ (row) (not (check-duplicates row))) + (map (curry filter-not (curry equal? ".")) array))) + +(define (check-rows board) + (scan-for-duplicates board)) + +(define (check-cols board) + (scan-for-duplicates (apply map list board))) + +(define (check-boxes board) + (define boxes-to-lists + (for*/list ([r (in-list '(0 3 6))] + [c (in-list '(0 3 6))]) + (for*/list ([box-r (in-range r (+ r 3))] + [box-c (in-range c (+ c 3))] + #:unless (equal? "." (pos board box-r box-c))) + (pos board box-r box-c)))) + (scan-for-duplicates boxes-to-lists)) + +(define/contract (is-valid-sudoku board) + (-> (listof (listof string?)) boolean?) + (and (check-rows board) + (check-cols board) + (check-boxes board))) + +(define valid-sudoku '[["5" "3" "." "." "7" "." "." "." "."] ["6" "." "." "1" "9" "5" "." "." "."] ["." "9" "8" "." "." "." "." "6" "."] ["8" "." "." "." "6" "." "." "." "3"] ["4" "." "." "8" "." "3" "." "." "1"] ["7" "." "." "." "2" "." "." "." "6"] ["." "6" "." "." "." "." "2" "8" "."] ["." "." "." "4" "1" "9" "." "." "5"] ["." "." "." "." "8" "." "." "7" "9"]]) +(define invalid-sudoku '[["8" "3" "." "." "7" "." "." "." "."] ["6" "." "." "1" "9" "5" "." "." "."] ["." "9" "8" "." "." "." "." "6" "."] ["8" "." "." "." "6" "." "." "." "3"] ["4" "." "." "8" "." "3" "." "." "1"] ["7" "." "." "." "2" "." "." "." "6"] ["." "6" "." "." "." "." "2" "8" "."] ["." "." "." "4" "1" "9" "." "." "5"] ["." "." "." "." "8" "." "." "7" "9"]]) \ No newline at end of file diff --git a/racket/leetcode/lc-415-add-strings.rkt b/racket/leetcode/lc-415-add-strings.rkt new file mode 100644 index 0000000..e140155 --- /dev/null +++ b/racket/leetcode/lc-415-add-strings.rkt @@ -0,0 +1,28 @@ +#lang racket + +(define/contract (add-strings num1 num2) + (-> string? string? string?) + (define (char->integer c) + ((compose string->number string) c)) + (define pad-length + (add1 (apply max (map string-length (list num1 num2))))) + (define (pad-with-zeroes n) + (~a n + #:align 'right + #:min-width pad-length + #:pad-string "0")) + (define (string-reverse s) + ((compose list->string reverse string->list) s)) + (define raw-sum + (for/fold ([sum-string ""] + [carry 0] + #:result sum-string) + ([n1 (string-reverse (pad-with-zeroes num1))] + [n2 (string-reverse (pad-with-zeroes num2))]) + (let* ([digit-sum (+ carry (char->integer n1) (char->integer n2))] + [sum-place (number->string (modulo digit-sum 10))] + [sum-carry (quotient digit-sum 10)]) + (values (string-append sum-place sum-string) + sum-carry)))) + (cond [(equal? raw-sum "00") "0"] + [else (string-trim raw-sum "0" #:repeat? #t #:right? #f)])) \ No newline at end of file diff --git a/racket/leetcode/lc-43-multiply-strings.rkt b/racket/leetcode/lc-43-multiply-strings.rkt new file mode 100644 index 0000000..dac8c31 --- /dev/null +++ b/racket/leetcode/lc-43-multiply-strings.rkt @@ -0,0 +1,28 @@ +#lang racket + +(define/contract (char-digit->integer c) + (-> char? integer?) + (- (char->integer c) 48)) + +(define/contract (integer->string-digit n) + (-> integer? string?) + (string (integer->char (+ n 48)))) + +(define/contract (number->string1 n [acc ""]) + (->* (integer?) (string?) string?) + (cond [(and (= n 0) (equal? acc "")) "0"] + [(= n 0) acc] + [else (number->string1 + (quotient n 10) + (string-append (integer->string-digit (remainder n 10)) acc))])) + +(define/contract (multiply num1 num2) + (-> string? string? string?) + (define multiplication-steps + (for/list ([n1 (in-string num1)] + [place1 (in-range (sub1 (string-length num1)) -1 -1)]) + (for/list ([n2 (in-string num2)] + [place2 (in-range (sub1 (string-length num2)) -1 -1)]) + (apply * (append (map char-digit->integer (list n1 n2)) + (list (expt 10 place1) (expt 10 place2))))))) + (number->string1 (apply + (flatten multiplication-steps)))) \ No newline at end of file diff --git a/racket/leetcode/lc-476-number-complement.rkt b/racket/leetcode/lc-476-number-complement.rkt new file mode 100644 index 0000000..724bb47 --- /dev/null +++ b/racket/leetcode/lc-476-number-complement.rkt @@ -0,0 +1,11 @@ +#lang racket + +(define (flip-bit bit) + (cond [(char=? bit #\1) #\0] + [(char=? bit #\0) #\1])) + +(define/contract (find-complement num) + (-> exact-integer? exact-integer?) + (define num-binary-list + (string->list (number->string num 2))) + (string->number (apply ~a (map flip-bit num-binary-list)) 2)) \ No newline at end of file diff --git a/racket/leetcode/lc-500-keyboard-row.rkt b/racket/leetcode/lc-500-keyboard-row.rkt new file mode 100644 index 0000000..5f13143 --- /dev/null +++ b/racket/leetcode/lc-500-keyboard-row.rkt @@ -0,0 +1,23 @@ +#lang racket + +(define keyboard-rows (list "qwertyuiop" + "asdfghjkl" + "zxcvbnm")) + +(define keyboard-row-sets + (for/list ([row keyboard-rows]) + (list->set (map string (string->list row))))) + +(define/contract (find-words words) + (-> (listof string?) (listof string?)) + (define word-checks + (for/list ([w words]) + (define word-set + (list->set (map string (string->list (string-downcase w))))) + (if (for/or ([row keyboard-row-sets]) + (subset? word-set row)) + w + '()))) + (filter-not empty? word-checks)) + +(find-words '("Hello" "Alaska" "Dad" "Peace")) \ No newline at end of file diff --git a/racket/leetcode/lc-504-base7.rkt b/racket/leetcode/lc-504-base7.rkt new file mode 100644 index 0000000..3e75052 --- /dev/null +++ b/racket/leetcode/lc-504-base7.rkt @@ -0,0 +1,16 @@ +#lang racket + +(define/contract (convert-to-base7 num) + (-> exact-integer? string?) + (define (max-base-power n base [pow 1]) + (cond [(n . = . (expt base pow)) pow] + [(n . < . (expt base pow)) (sub1 pow)] + [else (max-base-power n base (add1 pow))])) + (define (add-next-digit n pow acc) + (cond [(= pow 0) (string-append acc (number->string n))] + [else (add-next-digit (remainder n (expt 7 pow)) + (sub1 pow) + (string-append acc + (number->string (quotient n (expt 7 pow)))))])) + (string-append (if (negative? num) "-" "") + (add-next-digit (abs num) (max-base-power (abs num) 7) ""))) \ No newline at end of file diff --git a/racket/leetcode/lc-520-detect-capital.rkt b/racket/leetcode/lc-520-detect-capital.rkt new file mode 100644 index 0000000..80b5f7e --- /dev/null +++ b/racket/leetcode/lc-520-detect-capital.rkt @@ -0,0 +1,12 @@ +#lang racket + +(define/contract (detect-capital-use word) + (-> string? boolean?) + (if + (member word (list (string-upcase word) + (string-downcase word) + (string-titlecase word))) + #true + #false)) + +(detect-capital-use "Google") \ No newline at end of file diff --git a/racket/leetcode/lc-551-student-attendance-record-1.rkt b/racket/leetcode/lc-551-student-attendance-record-1.rkt new file mode 100644 index 0000000..c5f1456 --- /dev/null +++ b/racket/leetcode/lc-551-student-attendance-record-1.rkt @@ -0,0 +1,11 @@ +#lang racket + +(define/contract (check-record s) + (-> string? boolean?) + (define s-list (map string (string->list s))) + (cond [(<= 2 (count (curry string=? "A") s-list)) #false] + [(string-contains? s "LLL") #false] + [else #true])) + +(check-record "PPALLP") +(check-record "PPALLL") \ No newline at end of file diff --git a/racket/leetcode/lc-58-length-of-last-word.rkt b/racket/leetcode/lc-58-length-of-last-word.rkt new file mode 100644 index 0000000..716df90 --- /dev/null +++ b/racket/leetcode/lc-58-length-of-last-word.rkt @@ -0,0 +1,7 @@ +#lang racket + +(define/contract (length-of-last-word s) + (-> string? exact-integer?) + (if (empty? (string-split s)) + 0 + (string-length (last (string-split s))))) \ No newline at end of file diff --git a/racket/leetcode/lc-645-set-mismatch.rkt b/racket/leetcode/lc-645-set-mismatch.rkt new file mode 100644 index 0000000..a9d9a61 --- /dev/null +++ b/racket/leetcode/lc-645-set-mismatch.rkt @@ -0,0 +1,24 @@ +#lang racket + +(define/contract (find-error-nums nums) + (-> (listof exact-integer?) (listof exact-integer?)) + (define nums-set (list->set nums)) + (define range-set (apply set (range 1 (+ 2 (set-count nums-set))))) + (define missing-num (first (set->list (set-subtract range-set nums-set)))) + (define necessary-num + (if (set-member? nums-set (- missing-num 1)) + (+ missing-num 1) + (- missing-num 1))) + (list missing-num necessary-num)) + +(find-error-nums '(1 2 2 4)) + +(define fact-stream + (letrec ([f (lambda (x y) + (cond + [(zero? (modulo (- y 1) 3)) (cons (* 3 x) (lambda() (f (* x + y) (+ y 1))))] + [else (cons x (lambda() (f (* x y) (+ y 1))))]) + [else (cons x (lambda() (f (* x y) (+ y 1))))] + (lambda () (f 1 2)) + )]))) \ No newline at end of file diff --git a/racket/leetcode/lc-657-robot-return.rkt b/racket/leetcode/lc-657-robot-return.rkt new file mode 100644 index 0000000..908605a --- /dev/null +++ b/racket/leetcode/lc-657-robot-return.rkt @@ -0,0 +1,17 @@ +#lang racket + +(define/contract (judge-circle moves) + (-> string? boolean?) + (equal? '(0 0) + (for/fold ([y-pos 0] + [x-pos 0] + #:result (list y-pos x-pos)) + ([move (map string (string->list moves))]) + (values (case move + [("U") (add1 y-pos)] + [("D") (sub1 y-pos)] + [else y-pos]) + (case move + [("L") (add1 x-pos)] + [("R") (sub1 x-pos)] + [else x-pos]))))) \ No newline at end of file diff --git a/racket/leetcode/lc-68-justification.rkt b/racket/leetcode/lc-68-justification.rkt new file mode 100644 index 0000000..537e2c5 --- /dev/null +++ b/racket/leetcode/lc-68-justification.rkt @@ -0,0 +1,44 @@ +#lang racket +(define/contract (full-justify words max-width) + (-> (listof string?) exact-integer? (listof string?)) + + (define/contract (justify-line line [last-line #f]) + (->* ((listof string?)) (boolean?) string?) + (define gaps (sub1 (length line))) + (cond [last-line + (~a (string-join line " ") #:min-width max-width)] ; Right-pad the last line + [(= 1 (length line)) + (~a (first line) #:min-width max-width)] ; Right-pad single-word lines + [else + (let* ([words-length (apply + (map string-length line))] + [spacing-length (- max-width words-length)] ; How many spaces do we need? + [spacing-list (make-list gaps 1)] ; Every gap needs to be at least + [distribute (- spacing-length gaps)] ; 1 space long, so we need to + [distributed-spaces ; distribute the excess + (for/list ([space (in-list spacing-list)] + [i (in-naturals 1)]) + (+ space + (quotient distribute gaps) ; Add an equal number of spaces + (if (<= i ; to each gap, then add the + (modulo distribute gaps)) 1 0)))]) ; remainder at the front + (apply string-append + (append (map (λ (w s) + (string-append ; Knit together the first (n-1) + w (make-string s #\space))) ; words and gaps, then append + (drop-right line 1) ; the final word at the end + distributed-spaces) + (take-right line 1))))])) + + (for/fold ([lines '()] ; List of justified lines + [line-acc '()] ; Words to fit into the next line + #:result (append lines ; Only return the list of lines + (list (justify-line line-acc #t)))) ; and append the final line + ([word (in-list words)]) + (let* ([candidate-acc (append line-acc (list word))] + [candidate-length + (string-length (string-join candidate-acc " "))]) + (if (candidate-length . <= . max-width) ; If the word fits into the line, + (values lines ; keep the current line list + candidate-acc) ; and add it to the accumulator + (values (append lines (list (justify-line line-acc))) ; Otherwise, wrap up this line + (list word)))))) ; and start a new accumulator \ No newline at end of file diff --git a/racket/leetcode/lc-690-employee-importance.rkt b/racket/leetcode/lc-690-employee-importance.rkt new file mode 100644 index 0000000..1fb3fcc --- /dev/null +++ b/racket/leetcode/lc-690-employee-importance.rkt @@ -0,0 +1,14 @@ +#lang racket + +(define/contract (sum-even-after-queries A queries) + (-> (listof exact-integer?) + (listof (listof exact-integer?)) + (listof exact-integer?)) + (define array (list->vector A)) + (for/list ([query (in-list queries)]) + (vector-set! array + (second query) + (+ (first query) (vector-ref array (second query)))) + (for/sum ([element (vector-filter even? array)]) element))) + +(sum-even-after-queries '[1 2 3 4] '[[1 0] [-3 1] [-4 0] [2 3]]) \ No newline at end of file diff --git a/racket/leetcode/lc-717-1bit-and-2bit.rkt b/racket/leetcode/lc-717-1bit-and-2bit.rkt new file mode 100644 index 0000000..d9988ec --- /dev/null +++ b/racket/leetcode/lc-717-1bit-and-2bit.rkt @@ -0,0 +1,12 @@ +#lang racket + +(define/contract (is-one-bit-character bits) + (-> (listof exact-integer?) boolean?) + (define/match (check-next-character x . xs) + [(0 '()) #true] + [(1 (list _ ..2)) (apply check-next-character (cdr xs))] + [(0 (list _ ..1)) (apply check-next-character xs)] + [(_ _) #false]) + (apply check-next-character bits)) + +(is-one-bit-character (list 1 1 0 1 0)) \ No newline at end of file diff --git a/racket/leetcode/lc-745-prefix-suffix.rkt b/racket/leetcode/lc-745-prefix-suffix.rkt new file mode 100644 index 0000000..b01e3bb --- /dev/null +++ b/racket/leetcode/lc-745-prefix-suffix.rkt @@ -0,0 +1,27 @@ +#lang racket +(define word-filter% + (class object% + (super-new) + + ; words : (listof string?) + (init-field + words) ; Take in the provided dictionary. + (define word-ends-hash (make-hash)) ; Make an empty hash table. + + (for/list ([w (in-list words)] ; For each word in the dictionary, + [index (in-naturals)]) ; and its corresponding index, + (define len (string-length w)) ; calculate its length, + (for*/list ([head (in-range 1 (min 11 (add1 len)))] ; and for every combination of head length + [tail (in-range 1 (min 11 (add1 len)))]) ; and tail length + (hash-set! word-ends-hash ; from 1 to the max. affix length, + (list (substring w 0 head) ; set the key to the list containing + (substring w (- len tail) len)) ; the prefix and suffix + index))) ; and map it to the word's index. + + ; f : string? string? -> exact-integer? + (define/public (f prefix suffix) ; Return the mapped value for the affixes + (hash-ref word-ends-hash (list prefix suffix) -1)))) ; or -1 if it doesn't exist. + +;; Your word-filter% object will be instantiated and called as such: +;; (define obj (new word-filter% [words words])) +;; (define param_1 (send obj f prefix suffix)) \ No newline at end of file diff --git a/racket/leetcode/lc-747-largest-number-twice.rkt b/racket/leetcode/lc-747-largest-number-twice.rkt new file mode 100644 index 0000000..cea931b --- /dev/null +++ b/racket/leetcode/lc-747-largest-number-twice.rkt @@ -0,0 +1,15 @@ +#lang racket +(define/contract (dominant-index nums) + (-> (listof exact-integer?) exact-integer?) + (if (empty? (cdr nums)) + 0 + (let* ([indexed-list + (map cons nums (range (length nums)))] + [sorted-indexed-list + (sort indexed-list > #:key car)]) + (if ((car (first sorted-indexed-list)) . >= . (* 2 (car (second sorted-indexed-list)))) + (cdr (first sorted-indexed-list)) + -1)))) + +(dominant-index '(3 6 1 0)) +(dominant-index '(0)) \ No newline at end of file diff --git a/racket/leetcode/lc-766-toeplitz-matrix.rkt b/racket/leetcode/lc-766-toeplitz-matrix.rkt new file mode 100644 index 0000000..5606d2a --- /dev/null +++ b/racket/leetcode/lc-766-toeplitz-matrix.rkt @@ -0,0 +1,9 @@ +#lang racket + +(define/contract (is-toeplitz-matrix matrix) + (-> (listof (listof exact-integer?)) boolean?) + (cond [(empty? (cdr matrix)) #true] + [(equal? (drop-right (car matrix) 1) + (drop (cadr matrix) 1)) + (is-toeplitz-matrix (cdr matrix))] + [else #false])) \ No newline at end of file diff --git a/racket/leetcode/lc-771-jewels-and-stones.rkt b/racket/leetcode/lc-771-jewels-and-stones.rkt new file mode 100644 index 0000000..40ccf14 --- /dev/null +++ b/racket/leetcode/lc-771-jewels-and-stones.rkt @@ -0,0 +1,6 @@ +#lang racket + +(define/contract (num-jewels-in-stones jewels stones) + (-> string? string? exact-integer?) + (for/sum ([jewel (in-string jewels)]) + (count (curry char=? jewel) (string->list stones)))) \ No newline at end of file diff --git a/racket/leetcode/lc-788-rotated-digits.rkt b/racket/leetcode/lc-788-rotated-digits.rkt new file mode 100644 index 0000000..79400b8 --- /dev/null +++ b/racket/leetcode/lc-788-rotated-digits.rkt @@ -0,0 +1,17 @@ +#lang racket + +(define/contract (rotated-digits max-n) + (-> exact-integer? exact-integer?) + (for/fold ([good-number-count 0]) + ([n (in-range 1 (add1 max-n))]) + (if (is-good? n) + (add1 good-number-count) + good-number-count))) + +(define/contract (is-good? test-number) + (-> exact-integer? boolean?) + (define test-string (number->string test-number)) + (match test-string + [(regexp #rx"^[018]*$") #false] + [(regexp #rx"^[0125689]*$") #true] + [_ #false])) \ No newline at end of file diff --git a/racket/leetcode/lc-796-rotate-string.rkt b/racket/leetcode/lc-796-rotate-string.rkt new file mode 100644 index 0000000..b51d9e3 --- /dev/null +++ b/racket/leetcode/lc-796-rotate-string.rkt @@ -0,0 +1,6 @@ +#lang racket/base +(define/contract (rotate-string A B) + (-> string? string? boolean?) + (define doubled-A (string-append A A)) + (and (= (string-length A) (string-length B)) + (string-contains? doubled-A B))) \ No newline at end of file diff --git a/racket/leetcode/lc-819-most-common-word.rkt b/racket/leetcode/lc-819-most-common-word.rkt new file mode 100644 index 0000000..68a89c3 --- /dev/null +++ b/racket/leetcode/lc-819-most-common-word.rkt @@ -0,0 +1,14 @@ +#lang racket +(define/contract (most-common-word paragraph banned) + (-> string? (listof string?) string?) + (define word-count-hash (make-hash)) + (define banned-word-hash + (apply hash (flatten (map (λ (w) (cons w 'banned)) banned)))) + (define word-list + ((compose string-split string-downcase) + (string-replace paragraph #px"[^A-Za-z[:space:]]" " "))) + (for/list ([word (in-list word-list)]) + (cond [(hash-has-key? banned-word-hash word) void] + [else (hash-update! word-count-hash word add1 0)])) + (car (argmax cdr (hash->list word-count-hash)))) + diff --git a/racket/leetcode/lc-836-rectangle-overlap.rkt b/racket/leetcode/lc-836-rectangle-overlap.rkt new file mode 100644 index 0000000..ecdfb56 --- /dev/null +++ b/racket/leetcode/lc-836-rectangle-overlap.rkt @@ -0,0 +1,14 @@ +#lang racket +(define (rectangle-area lst) + (match-define (list x1 y1 x2 y2) lst) + (* (- x2 x1) (- y2 y1))) + +(define/contract (is-rectangle-overlap rec1 rec2) + (-> (listof exact-integer?) (listof exact-integer?) boolean?) + (cond [(or (= 0 (rectangle-area rec1)) + (= 0 (rectangle-area rec2))) #false] + [(not (or ((list-ref rec1 2) . <= . (list-ref rec2 0)) + ((list-ref rec1 3) . <= . (list-ref rec2 1)) + ((list-ref rec1 0) . >= . (list-ref rec2 2)) + ((list-ref rec1 1) . >= . (list-ref rec2 3)))) #true] + [else #false])) \ No newline at end of file diff --git a/racket/leetcode/lc-844-backspace-string-compare.rkt b/racket/leetcode/lc-844-backspace-string-compare.rkt new file mode 100644 index 0000000..a07ec3c --- /dev/null +++ b/racket/leetcode/lc-844-backspace-string-compare.rkt @@ -0,0 +1,17 @@ +#lang racket + +(define/contract (process-backspace-string strng) + (-> string? string?) + (apply ~a (for/fold ([str-out '()] + #:result (reverse str-out)) + ([character (in-string strng)]) + (case character + [(#\#) (if (empty? str-out) + str-out + (cdr str-out))] + [else (cons character str-out)])))) + +(define/contract (backspace-compare s t) + (-> string? string? boolean?) + (equal? (process-backspace-string s) + (process-backspace-string t))) \ No newline at end of file diff --git a/racket/leetcode/lc-896-monotonic-array.rkt b/racket/leetcode/lc-896-monotonic-array.rkt new file mode 100644 index 0000000..fa43244 --- /dev/null +++ b/racket/leetcode/lc-896-monotonic-array.rkt @@ -0,0 +1,16 @@ +#lang racket +(define/contract (is-monotonic test-list) + (-> (listof exact-integer?) boolean?) + (cond [(empty? (cdr test-list)) #true] + [((car test-list) . > . (cadr test-list)) + (is-monotonic-direction test-list >=)] + [((car test-list) . < . (cadr test-list)) + (is-monotonic-direction test-list <=)] + [else (is-monotonic (cdr test-list))])) + +(define/contract (is-monotonic-direction test-list dir) + (-> (listof exact-integer?) procedure? boolean?) + (cond [(empty? (cdr test-list)) #true] + [((car test-list) . dir . (cadr test-list)) + (is-monotonic-direction (cdr test-list) dir)] + [else #false])) \ No newline at end of file diff --git a/racket/leetcode/lc-9-palindromic-number.rkt b/racket/leetcode/lc-9-palindromic-number.rkt new file mode 100644 index 0000000..1fccbef --- /dev/null +++ b/racket/leetcode/lc-9-palindromic-number.rkt @@ -0,0 +1,38 @@ +#lang racket +(define/contract (is-palindrome x) + (-> exact-integer? boolean?) + ; get the easy cases out of the way first + ; negative numbers are not palindromes, single-digit numbers are + (cond [(x . < . 0) #false] + [(x . < . 10) #true] + [else + ; order-of-magnitude returns the scientific notation exponent + ; so add 1 to get the number of digits + (define digits + (add1 (order-of-magnitude x))) + ; figure out how many digits we need to trim to find the mirrored halves + ; if there are an even number of digits 2n, we will remove n of them from the right + ; if there are an odd number of digits 2n+1, we will remove n+1 of them from the right + (define half-digits + (cond [(even? digits) (/ digits 2)] + [else (/ (add1 digits) 2)])) + ; divide x by a power of 10 to get the digits to match + (define front-half + (quotient x (expt 10 half-digits))) + ; reverse the back half with repeated divisions by 10 + (define back-half-reversed + (for/fold ([reversed 0] + [remaining (remainder x (expt 10 half-digits))] + ; build up the sum of the digits in reversed and return it at the end + #:result reversed) + ; if we have an odd number of digits, we don't need to match the middle one + ([n (in-range (if (even? digits) + half-digits + (sub1 half-digits)))]) + ; shift all the accumulated digits in the mirror to the left one place + ; and add the next one to the right, + ; then chop the right-most digit off the original + (values (+ (* 10 reversed) (remainder remaining 10)) + (quotient remaining 10)))) + ; finally, check to see if the mirrored right is equal to the original left + (= front-half back-half-reversed)])) diff --git a/racket/leetcode/lc-905-sort-by-parity.rkt b/racket/leetcode/lc-905-sort-by-parity.rkt new file mode 100644 index 0000000..7c736f3 --- /dev/null +++ b/racket/leetcode/lc-905-sort-by-parity.rkt @@ -0,0 +1,7 @@ +#lang racket +(define/contract (sort-array-by-parity A) + (-> (listof exact-integer?) (listof exact-integer?)) + ((compose flatten (if (odd? (car A)) reverse identity)) + (group-by (λ (n) (modulo n 2)) A))) + +(sort-array-by-parity '(1 1 3 4)) \ No newline at end of file diff --git a/racket/leetcode/lc-944-delete-columns.rkt b/racket/leetcode/lc-944-delete-columns.rkt new file mode 100644 index 0000000..f9714b7 --- /dev/null +++ b/racket/leetcode/lc-944-delete-columns.rkt @@ -0,0 +1,7 @@ +#lang racket +(define/contract (min-deletion-size strs) + (-> (listof string?) exact-integer?) + (define transposed-strs (apply map list (map string->list strs))) + (count (λ (s) (not (equal? s (sort s char (listof string?) string? boolean?) + (define alpha-order + (make-hash (map (λ (a b) (cons a b)) + (map string (string->list order)) + (build-list (string-length order) identity)))) + (hash-set! alpha-order #\* -1) + (define (alien (listof exact-integer?) exact-integer? (listof exact-integer?)) + (define rev-num (reverse num)) + (define raw-array-form + (cond [(= k 0) num] + [else + (for/fold ([sum-list '()] + [addend k] + [carry 0] + #:result sum-list) + ([place (append rev-num + (make-list (add1 (order-of-magnitude k)) 0))]) + (let* ([place-sum (+ place carry (modulo addend 10))] + [to-carry (quotient place-sum 10)] + [new-place (modulo place-sum 10)]) + (values (cons new-place sum-list) + (quotient addend 10) + to-carry)))])) + (match-define-values + (result _) (drop-common-prefix raw-array-form + (make-list (length raw-array-form) 0))) + (cond [(empty? result) '(0)] + [else result])) \ No newline at end of file diff --git a/racket/leetcode/lc-999-available-captures.rkt b/racket/leetcode/lc-999-available-captures.rkt new file mode 100644 index 0000000..1b1a3a9 --- /dev/null +++ b/racket/leetcode/lc-999-available-captures.rkt @@ -0,0 +1,28 @@ +#lang racket +(define/contract (num-rook-captures board) + (-> (listof (listof string?)) exact-integer?) + + (define (get-rook-space [board-state board]) + (for/or ([board-rank (in-list board-state)] + [rank (in-range 0 (length board-state))] + #:when (index-of board-rank "R")) + (list rank (index-of board-rank "R")))) + + (define (check-for-capturable-pawns spaces) + (match spaces + [(list _ ... "p" "." ... "R" "." ... "p" _ ...) 2] + [(list _ ... "R" "." ... "p" _ ...) 1] + [(list _ ... "p" "." ... "R" _ ...) 1] + [_ 0])) + + (define (check-rank rank [board-state board]) + (let ([spaces (list-ref board-state rank)]) + (check-for-capturable-pawns spaces))) + + (define (check-file file [board-state board]) + (let ([spaces (map (curryr list-ref file) board)]) + (check-for-capturable-pawns spaces))) + + (match (get-rook-space board) + [(list rank file) (+ (check-rank rank) + (check-file file))])) \ No newline at end of file -- cgit v1.2.3