From 908b362ee52491a1a291bf352f1a3b1792fe7694 Mon Sep 17 00:00:00 2001 From: Thraix Date: Wed, 26 Nov 2025 22:43:11 +0100 Subject: [PATCH] Add 2023 solutions --- res/2023/day01/test_input.txt | 7 + res/2023/day02/test_input.txt | 5 + res/2023/day03/test_input.txt | 10 ++ res/2023/day04/test_input.txt | 6 + res/2023/day05/test_input.txt | 33 ++++ res/2023/day06/test_input.txt | 2 + res/2023/day07/test_input.txt | 5 + res/2023/day08/test_input.txt | 10 ++ res/2023/day09/test_input.txt | 3 + res/2023/day10/test_input.txt | 10 ++ res/2023/day11/test_input.txt | 10 ++ res/2023/day12/test_input.txt | 6 + res/2023/day13/test_input.txt | 15 ++ res/2023/day14/test_input.txt | 10 ++ res/2023/day15/test_input.txt | 1 + res/2023/day16/test_input.txt | 10 ++ res/2023/day17/test_input.txt | 13 ++ res/2023/day18/test_input.txt | 14 ++ res/2023/day19/test_input.txt | 17 ++ res/2023/day20/test_input.txt | 5 + res/2023/day21/test_input.txt | 11 ++ res/2023/day22/test_input.txt | 7 + res/2023/day23/test_input.txt | 23 +++ res/2023/day24/test_input.txt | 5 + res/2023/day25/test_input.txt | 13 ++ src/2023/Day01.cpp | 95 ++++++++++++ src/2023/Day02.cpp | 88 +++++++++++ src/2023/Day03.cpp | 116 ++++++++++++++ src/2023/Day04.cpp | 89 +++++++++++ src/2023/Day05.cpp | 157 +++++++++++++++++++ src/2023/Day06.cpp | 69 +++++++++ src/2023/Day07.cpp | 155 +++++++++++++++++++ src/2023/Day08.cpp | 83 ++++++++++ src/2023/Day09.cpp | 83 ++++++++++ src/2023/Day10.cpp | 195 +++++++++++++++++++++++ src/2023/Day11.cpp | 95 ++++++++++++ src/2023/Day12.cpp | 120 ++++++++++++++ src/2023/Day13.cpp | 137 ++++++++++++++++ src/2023/Day14.cpp | 161 +++++++++++++++++++ src/2023/Day15.cpp | 89 +++++++++++ src/2023/Day16.cpp | 117 ++++++++++++++ src/2023/Day17.cpp | 103 ++++++++++++ src/2023/Day18.cpp | 105 +++++++++++++ src/2023/Day19.cpp | 151 ++++++++++++++++++ src/2023/Day20.cpp | 267 ++++++++++++++++++++++++++++++++ src/2023/Day21.cpp | 98 ++++++++++++ src/2023/Day22.cpp | 169 ++++++++++++++++++++ src/2023/Day23.cpp | 176 +++++++++++++++++++++ src/2023/Day24.cpp | 284 ++++++++++++++++++++++++++++++++++ src/2023/Day25.cpp | 117 ++++++++++++++ src/common/aoc.h | 1 + 51 files changed, 3571 insertions(+) create mode 100644 res/2023/day01/test_input.txt create mode 100644 res/2023/day02/test_input.txt create mode 100644 res/2023/day03/test_input.txt create mode 100644 res/2023/day04/test_input.txt create mode 100644 res/2023/day05/test_input.txt create mode 100644 res/2023/day06/test_input.txt create mode 100644 res/2023/day07/test_input.txt create mode 100644 res/2023/day08/test_input.txt create mode 100644 res/2023/day09/test_input.txt create mode 100644 res/2023/day10/test_input.txt create mode 100644 res/2023/day11/test_input.txt create mode 100644 res/2023/day12/test_input.txt create mode 100644 res/2023/day13/test_input.txt create mode 100644 res/2023/day14/test_input.txt create mode 100644 res/2023/day15/test_input.txt create mode 100644 res/2023/day16/test_input.txt create mode 100644 res/2023/day17/test_input.txt create mode 100644 res/2023/day18/test_input.txt create mode 100644 res/2023/day19/test_input.txt create mode 100644 res/2023/day20/test_input.txt create mode 100644 res/2023/day21/test_input.txt create mode 100644 res/2023/day22/test_input.txt create mode 100644 res/2023/day23/test_input.txt create mode 100644 res/2023/day24/test_input.txt create mode 100644 res/2023/day25/test_input.txt create mode 100644 src/2023/Day01.cpp create mode 100644 src/2023/Day02.cpp create mode 100644 src/2023/Day03.cpp create mode 100644 src/2023/Day04.cpp create mode 100644 src/2023/Day05.cpp create mode 100644 src/2023/Day06.cpp create mode 100644 src/2023/Day07.cpp create mode 100644 src/2023/Day08.cpp create mode 100644 src/2023/Day09.cpp create mode 100644 src/2023/Day10.cpp create mode 100644 src/2023/Day11.cpp create mode 100644 src/2023/Day12.cpp create mode 100644 src/2023/Day13.cpp create mode 100644 src/2023/Day14.cpp create mode 100644 src/2023/Day15.cpp create mode 100644 src/2023/Day16.cpp create mode 100644 src/2023/Day17.cpp create mode 100644 src/2023/Day18.cpp create mode 100644 src/2023/Day19.cpp create mode 100644 src/2023/Day20.cpp create mode 100644 src/2023/Day21.cpp create mode 100644 src/2023/Day22.cpp create mode 100644 src/2023/Day23.cpp create mode 100644 src/2023/Day24.cpp create mode 100644 src/2023/Day25.cpp diff --git a/res/2023/day01/test_input.txt b/res/2023/day01/test_input.txt new file mode 100644 index 0000000..4316a6b --- /dev/null +++ b/res/2023/day01/test_input.txt @@ -0,0 +1,7 @@ +two1nine +eightwothree +abcone2threexyz +xtwone3four +4nineeightseven2 +zoneight234 +7pqrstsixteen \ No newline at end of file diff --git a/res/2023/day02/test_input.txt b/res/2023/day02/test_input.txt new file mode 100644 index 0000000..1cd7d33 --- /dev/null +++ b/res/2023/day02/test_input.txt @@ -0,0 +1,5 @@ +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 \ No newline at end of file diff --git a/res/2023/day03/test_input.txt b/res/2023/day03/test_input.txt new file mode 100644 index 0000000..624ea4f --- /dev/null +++ b/res/2023/day03/test_input.txt @@ -0,0 +1,10 @@ +467..114.. +...*...... +..35..633. +......#... +617*...... +.....+.58. +..592..... +......755. +...$.*.... +.664.598.. \ No newline at end of file diff --git a/res/2023/day04/test_input.txt b/res/2023/day04/test_input.txt new file mode 100644 index 0000000..71f208a --- /dev/null +++ b/res/2023/day04/test_input.txt @@ -0,0 +1,6 @@ +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 \ No newline at end of file diff --git a/res/2023/day05/test_input.txt b/res/2023/day05/test_input.txt new file mode 100644 index 0000000..bd902a4 --- /dev/null +++ b/res/2023/day05/test_input.txt @@ -0,0 +1,33 @@ +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 \ No newline at end of file diff --git a/res/2023/day06/test_input.txt b/res/2023/day06/test_input.txt new file mode 100644 index 0000000..b39f49d --- /dev/null +++ b/res/2023/day06/test_input.txt @@ -0,0 +1,2 @@ +Time: 7 15 30 +Distance: 9 40 200 \ No newline at end of file diff --git a/res/2023/day07/test_input.txt b/res/2023/day07/test_input.txt new file mode 100644 index 0000000..bf2815e --- /dev/null +++ b/res/2023/day07/test_input.txt @@ -0,0 +1,5 @@ +32T3K 765 +T55J5 684 +KK677 28 +KTJJT 220 +QQQJA 483 \ No newline at end of file diff --git a/res/2023/day08/test_input.txt b/res/2023/day08/test_input.txt new file mode 100644 index 0000000..51a710e --- /dev/null +++ b/res/2023/day08/test_input.txt @@ -0,0 +1,10 @@ +LR + +AAA = (11B, XXX) +11B = (XXX, ZZZ) +ZZZ = (11B, XXX) +22A = (22B, XXX) +22B = (22C, 22C) +22C = (22Z, 22Z) +22Z = (22B, 22B) +XXX = (XXX, XXX) diff --git a/res/2023/day09/test_input.txt b/res/2023/day09/test_input.txt new file mode 100644 index 0000000..70c5595 --- /dev/null +++ b/res/2023/day09/test_input.txt @@ -0,0 +1,3 @@ +0 3 6 9 12 15 +1 3 6 10 15 21 +10 13 16 21 30 45 \ No newline at end of file diff --git a/res/2023/day10/test_input.txt b/res/2023/day10/test_input.txt new file mode 100644 index 0000000..adaae96 --- /dev/null +++ b/res/2023/day10/test_input.txt @@ -0,0 +1,10 @@ +.F----7F7F7F7F-7.... +.|F--7||||||||FJ.... +.||.FJ||||||||L7.... +FJL7L7LJLJ||LJ.L-7.. +L--J.L7...LJS7F-7L7. +....F-J..F7FJ|L7L7L7 +....L7.F7||L7|.L7L7| +.....|FJLJ|FJ|F7|.LJ +....FJL-7.||.||||... +....L---J.LJ.LJLJ... diff --git a/res/2023/day11/test_input.txt b/res/2023/day11/test_input.txt new file mode 100644 index 0000000..986aad4 --- /dev/null +++ b/res/2023/day11/test_input.txt @@ -0,0 +1,10 @@ +...#...... +.......#.. +#......... +.......... +......#... +.#........ +.........# +.......... +.......#.. +#...#..... diff --git a/res/2023/day12/test_input.txt b/res/2023/day12/test_input.txt new file mode 100644 index 0000000..c5bec3a --- /dev/null +++ b/res/2023/day12/test_input.txt @@ -0,0 +1,6 @@ +???.### 1,1,3 +.??..??...?##. 1,1,3 +?#?#?#?#?#?#?#? 1,3,1,6 +????.#...#... 4,1,1 +????.######..#####. 1,6,5 +?###???????? 3,2,1 \ No newline at end of file diff --git a/res/2023/day13/test_input.txt b/res/2023/day13/test_input.txt new file mode 100644 index 0000000..3b6b5cc --- /dev/null +++ b/res/2023/day13/test_input.txt @@ -0,0 +1,15 @@ +#.##..##. +..#.##.#. +##......# +##......# +..#.##.#. +..##..##. +#.#.##.#. + +#...##..# +#....#..# +..##..### +#####.##. +#####.##. +..##..### +#....#..# diff --git a/res/2023/day14/test_input.txt b/res/2023/day14/test_input.txt new file mode 100644 index 0000000..5a24dce --- /dev/null +++ b/res/2023/day14/test_input.txt @@ -0,0 +1,10 @@ +O....#.... +O.OO#....# +.....##... +OO.#O....O +.O.....O#. +O.#..O.#.# +..O..#O..O +.......O.. +#....###.. +#OO..#.... diff --git a/res/2023/day15/test_input.txt b/res/2023/day15/test_input.txt new file mode 100644 index 0000000..62f7ed0 --- /dev/null +++ b/res/2023/day15/test_input.txt @@ -0,0 +1 @@ +rn=1,cm-,qp=3,cm=2,qp-,pc=4,ot=9,ab=5,pc-,pc=6,ot=7 \ No newline at end of file diff --git a/res/2023/day16/test_input.txt b/res/2023/day16/test_input.txt new file mode 100644 index 0000000..c78b2e7 --- /dev/null +++ b/res/2023/day16/test_input.txt @@ -0,0 +1,10 @@ +.|...\.... +|.-.\..... +.....|-... +........|. +.......... +.........\ +..../.\\.. +.-.-/..|.. +.|....-|.\ +..//.|.... \ No newline at end of file diff --git a/res/2023/day17/test_input.txt b/res/2023/day17/test_input.txt new file mode 100644 index 0000000..3c85086 --- /dev/null +++ b/res/2023/day17/test_input.txt @@ -0,0 +1,13 @@ +2413432311323 +3215453535623 +3255245654254 +3446585845452 +4546657867536 +1438598798454 +4457876987766 +3637877979653 +4654967986887 +4564679986453 +1224686865563 +2546548887735 +4322674655533 \ No newline at end of file diff --git a/res/2023/day18/test_input.txt b/res/2023/day18/test_input.txt new file mode 100644 index 0000000..0ad754b --- /dev/null +++ b/res/2023/day18/test_input.txt @@ -0,0 +1,14 @@ +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) \ No newline at end of file diff --git a/res/2023/day19/test_input.txt b/res/2023/day19/test_input.txt new file mode 100644 index 0000000..a08746e --- /dev/null +++ b/res/2023/day19/test_input.txt @@ -0,0 +1,17 @@ +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} \ No newline at end of file diff --git a/res/2023/day20/test_input.txt b/res/2023/day20/test_input.txt new file mode 100644 index 0000000..4da4379 --- /dev/null +++ b/res/2023/day20/test_input.txt @@ -0,0 +1,5 @@ +broadcaster -> a +%a -> inv, con +&inv -> b +%b -> con +&con -> output \ No newline at end of file diff --git a/res/2023/day21/test_input.txt b/res/2023/day21/test_input.txt new file mode 100644 index 0000000..ed0fc41 --- /dev/null +++ b/res/2023/day21/test_input.txt @@ -0,0 +1,11 @@ +........... +......##.#. +.###..#..#. +..#.#...#.. +....#.#.... +.....S..... +.##......#. +.......##.. +.##.#.####. +.##...#.##. +........... \ No newline at end of file diff --git a/res/2023/day22/test_input.txt b/res/2023/day22/test_input.txt new file mode 100644 index 0000000..0edd90f --- /dev/null +++ b/res/2023/day22/test_input.txt @@ -0,0 +1,7 @@ +1,0,1~1,2,1 +1,1,8~1,1,9 +0,2,3~2,2,3 +0,0,4~0,2,4 +0,0,2~2,0,2 +2,0,5~2,2,5 +0,1,6~2,1,6 \ No newline at end of file diff --git a/res/2023/day23/test_input.txt b/res/2023/day23/test_input.txt new file mode 100644 index 0000000..65c7c3d --- /dev/null +++ b/res/2023/day23/test_input.txt @@ -0,0 +1,23 @@ +#.##################### +#.......#########...### +#######.#########.#.### +###.....#.>.>.###.#.### +###v#####.#v#.###.#.### +###.>...#.#.#.....#...# +###v###.#.#.#########.# +###...#.#.#.......#...# +#####.#.#.#######.#.### +#.....#.#.#.......#...# +#.#####.#.#.#########v# +#.#...#...#...###...>.# +#.#.#v#######v###.###v# +#...#.>.#...>.>.#.###.# +#####v#.#.###v#.#.###.# +#.....#...#...#.#.#...# +#.#########.###.#.#.### +#...###...#...#...#.### +###.###.#.###v#####v### +#...#...#.#.>.>.#.>.### +#.###.###.#.###.#.#v### +#.....###...###...#...# +#####################.# \ No newline at end of file diff --git a/res/2023/day24/test_input.txt b/res/2023/day24/test_input.txt new file mode 100644 index 0000000..cbe1492 --- /dev/null +++ b/res/2023/day24/test_input.txt @@ -0,0 +1,5 @@ +19, 13, 30 @ -2, 1, -2 +18, 19, 22 @ -1, -1, -2 +20, 25, 34 @ -2, -2, -4 +12, 31, 28 @ -1, -2, -1 +20, 19, 15 @ 1, -5, -3 \ No newline at end of file diff --git a/res/2023/day25/test_input.txt b/res/2023/day25/test_input.txt new file mode 100644 index 0000000..f5bdcc2 --- /dev/null +++ b/res/2023/day25/test_input.txt @@ -0,0 +1,13 @@ +jqt: rhn xhk nvd +rsh: frs pzl lsr +xhk: hfx +cmg: qnr nvd lhk bvb +rhn: xhk bvb hfx +bvb: xhk hfx +pzl: lsr hfx nvd +qnr: nvd +ntq: jqt hfx bvb xhk +nvd: lhk +lsr: lhk +rzs: qnr cmg lsr rsh +frs: qnr lhk lsr \ No newline at end of file diff --git a/src/2023/Day01.cpp b/src/2023/Day01.cpp new file mode 100644 index 0000000..cdd89d3 --- /dev/null +++ b/src/2023/Day01.cpp @@ -0,0 +1,95 @@ +#include "common/aoc.h" + +namespace y2023::day01 +{ + REGISTER_DAY(2023, Day01, std::vector, int); + + REGISTER_TEST_EXAMPLE(2023, Day01, ExampleInput, 1, 209); + REGISTER_TEST(2023, Day01, Input, 1, 54990); + REGISTER_TEST_EXAMPLE(2023, Day01, ExampleInput, 2, 281); + REGISTER_TEST(2023, Day01, Input, 2, 54473); + + int GetNumberAt(const std::string& str, int pos) + { + if (str[pos] >= '0' && str[pos] <= '9') + { + return str[pos] - '0'; + } + else + { + static const std::vector numbers{ + "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"}; + for (int num = 0; num < numbers.size(); num++) + { + if (Helper::StartsWith(std::string_view{&str[pos], str.size() - pos}, numbers[num])) + { + return num + 1; + } + } + } + return -1; + } + + READ_INPUT(input) + { + return Input::ReadLines(input); + } + + OUTPUT1(input) + { + int sum = 0; + for (auto& str : input) + { + int first = 0; + int last = 0; + for (int i = 0; i < str.size(); i++) + { + if (Helper::IsDigit(str[i])) + { + first = str[i] - '0'; + break; + } + } + for (int i = str.size() - 1; i >= 0; i--) + { + if (Helper::IsDigit(str[i])) + { + last = str[i] - '0'; + break; + } + } + sum += first * 10 + last; + } + return sum; + } + + OUTPUT2(input) + { + int sum = 0; + for (auto& str : input) + { + int first = -1; + int last = -1; + for (int i = 0; i < str.size(); i++) + { + int number = GetNumberAt(str, i); + if (number != -1) + { + first = number; + break; + } + } + for (int i = str.size() - 1; i >= 0; i--) + { + int number = GetNumberAt(str, i); + if (number != -1) + { + last = number; + break; + } + } + sum += first * 10 + last; + } + return sum; + } +} diff --git a/src/2023/Day02.cpp b/src/2023/Day02.cpp new file mode 100644 index 0000000..d98b7d7 --- /dev/null +++ b/src/2023/Day02.cpp @@ -0,0 +1,88 @@ +#include "common/aoc.h" + +namespace y2023::day02 +{ + struct Set + { + int red = 0; + int green = 0; + int blue = 0; + }; + + REGISTER_DAY(2023, Day02, std::vector>, int); + + REGISTER_TEST_EXAMPLE(2023, Day02, ExampleInput, 1, 8); + REGISTER_TEST(2023, Day02, Input, 1, 2164); + REGISTER_TEST_EXAMPLE(2023, Day02, ExampleInput, 2, 2286); + REGISTER_TEST(2023, Day02, Input, 2, 69929); + + READ_INPUT(input) + { + std::string str; + std::vector> games; + while (std::getline(input, str)) + { + std::vector sets{Set{}}; + std::stringstream ss{str}; + int i = 0; + ss >> "Game " >> i >> ": "; + while (true) + { + int count; + std::string col; + ss >> count >> col; + if (col.front() == 'r') + sets.back().red = count; + if (col.front() == 'g') + sets.back().green = count; + if (col.front() == 'b') + sets.back().blue = count; + if (col.back() == ';') + sets.emplace_back(Set{}); + else if (col.back() == ',') + ; + else + break; + } + games.emplace_back(sets); + } + return games; + } + + OUTPUT1(input) + { + int sum = 0; + for (int i = 0; i < input.size(); i++) + { + bool valid = true; + for (auto& set : input[i]) + { + if (set.red > 12 || set.green > 13 || set.blue > 14) + { + valid = false; + break; + } + } + if (valid) + sum += i + 1; + } + return sum; + } + + OUTPUT2(input) + { + int sum = 0; + for (int i = 0; i < input.size(); i++) + { + Set minSet; + for (auto& set : input[i]) + { + minSet.red = std::max(minSet.red, set.red); + minSet.green = std::max(minSet.green, set.green); + minSet.blue = std::max(minSet.blue, set.blue); + } + sum += minSet.red * minSet.green * minSet.blue; + } + return sum; + } +} diff --git a/src/2023/Day03.cpp b/src/2023/Day03.cpp new file mode 100644 index 0000000..4271c13 --- /dev/null +++ b/src/2023/Day03.cpp @@ -0,0 +1,116 @@ +#include "common/aoc.h" + +namespace y2023::day03 +{ + REGISTER_DAY(2023, Day03, Array2D, int); + + REGISTER_TEST_EXAMPLE(2023, Day03, ExampleInput, 1, 4361); + REGISTER_TEST(2023, Day03, Input, 1, 549908); + REGISTER_TEST_EXAMPLE(2023, Day03, ExampleInput, 2, 467835); + REGISTER_TEST(2023, Day03, Input, 2, 81166799); + + bool HasAdjecent(const Array2D& array, Index2D index, int width) + { + for (int y = index.y - 1; y <= index.y + 1; y++) + { + if (!array.IsInside(Index2D{index.x, y})) + continue; + + for (int x = index.x - 1; x < index.x + width + 1; x++) + { + if (!array.IsInside(Index2D{x, y})) + continue; + + if (array.Get(x, y) != '.' && !Helper::IsDigit(array.Get(x, y))) + return true; + } + } + return false; + } + + int GetNumber(const Array2D& array, Index2D index) + { + int number = 0; + while (index.x < array.width && Helper::IsDigit(array.Get(index))) + { + number = number * 10 + array.Get(index) - '0'; + index.x++; + } + return number; + } + + int GetGearRatio(const Array2D& array, Index2D index) + { + int firstNum = 0; + for (int y = index.y - 1; y <= index.y + 1; y++) + { + if (!array.IsInside(Index2D{index.x, y})) + continue; + + for (int x = index.x - 1; x <= index.x + 1; x++) + { + if (!array.IsInside(Index2D{x, y})) + continue; + + if (Helper::IsDigit(array.Get(x, y))) + { + Index2D numberStart = Index2D{x, y}; + while (numberStart.x >= 0 && Helper::IsDigit(array.Get(numberStart))) numberStart.x--; + numberStart.x++; + + int number = GetNumber(array, numberStart); + if (firstNum == 0) + firstNum = number; + else + return firstNum * number; + + while (x < array.width && Helper::IsDigit(array.Get(x, y))) x++; + x--; + } + } + } + + return 0; + } + + READ_INPUT(input) + { + return Input::ReadArray2D(input); + } + + OUTPUT1(input) + { + int sum = 0; + for (int y = 0; y < input.height; y++) + { + for (int x = 0; x < input.width; x++) + { + if (Helper::IsDigit(input.Get(x, y))) + { + int i = 1; + while (x + i < input.width && Helper::IsDigit(input.Get(x + i, y))) i++; + if (HasAdjecent(input, Index2D{x, y}, i)) + { + sum += GetNumber(input, Index2D{x, y}); + } + x += i - 1; + } + } + } + return sum; + } + + OUTPUT2(input) + { + int sum = 0; + input.Each( + [&](const Array2D& array, const Index2D& index) + { + if (array.Get(index) == '*') + { + sum += GetGearRatio(input, index); + } + }); + return sum; + } +} diff --git a/src/2023/Day04.cpp b/src/2023/Day04.cpp new file mode 100644 index 0000000..2d0c8f0 --- /dev/null +++ b/src/2023/Day04.cpp @@ -0,0 +1,89 @@ +#include "common/aoc.h" + +namespace y2023::day04 +{ + + struct Game + { + std::vector winning; + std::set numbers; + }; + + REGISTER_DAY(2023, Day04, std::vector, int); + + REGISTER_TEST_EXAMPLE(2023, Day04, ExampleInput, 1, 13); + REGISTER_TEST(2023, Day04, Input, 1, 23678); + REGISTER_TEST_EXAMPLE(2023, Day04, ExampleInput, 2, 30); + REGISTER_TEST(2023, Day04, Input, 2, 15455663); + + READ_INPUT(input) + { + std::string str; + std::vector games; + while (std::getline(input, str)) + { + Game game; + int i; + int pos = str.find("|"); + + std::stringstream ss{str}; + ss >> "Card " >> i >> ":"; + while (ss >> i) game.winning.emplace_back(i); + ss.clear(); // clear the failbit to be able to read more data + + ss >> "|"; + + while (ss >> i) game.numbers.emplace(i); + + games.emplace_back(game); + } + return games; + } + + OUTPUT1(input) + { + int sum = 0; + for (auto& game : input) + { + int score = 0; + for (int winning : game.winning) + { + if (game.numbers.count(winning)) + { + if (score == 0) + score = 1; + else + score *= 2; + } + } + sum += score; + } + return sum; + } + + OUTPUT2(input) + { + std::vector scratches(input.size(), 1); + int sum = 0; + for (int game = 0; game < input.size(); game++) + { + int matches = 0; + for (int winning : input[game].winning) + { + if (input[game].numbers.count(winning)) + { + matches++; + } + } + for (int i = 0; i < matches; i++) + { + scratches[game + i + 1] += scratches[game]; + } + } + for (int i = 0; i < scratches.size(); i++) + { + sum += scratches[i]; + } + return sum; + } +} diff --git a/src/2023/Day05.cpp b/src/2023/Day05.cpp new file mode 100644 index 0000000..c89b335 --- /dev/null +++ b/src/2023/Day05.cpp @@ -0,0 +1,157 @@ +#include "common/aoc.h" + +namespace y2023::day05 +{ + struct Range + { + int64_t start; + int64_t length; + + friend std::ostream& operator<<(std::ostream& stream, const Range& range) + { + return stream << range.start << " " << range.length; + } + }; + + struct Plantation + { + std::vector seeds; + std::vector> maps; + }; + + REGISTER_DAY(2023, Day05, Plantation, int64_t); + + REGISTER_TEST_EXAMPLE(2023, Day05, ExampleInput, 1, 35); + REGISTER_TEST(2023, Day05, Input, 1, 88151870); + REGISTER_TEST_EXAMPLE(2023, Day05, ExampleInput, 2, 46); + REGISTER_TEST(2023, Day05, Input, 2, 2008785); + + int64_t FindMap(const std::map& map, int64_t value) + { + for (auto& range : map) + { + if (value >= range.first && value < range.first + range.second.length) + { + return range.second.start + (value - range.first); + } + } + return value; + } + + int64_t FindLocation(const Plantation& plantation, int64_t seed) + { + int64_t value = seed; + for (auto& ranges : plantation.maps) + { + value = FindMap(ranges, value); + } + return value; + } + + Range FindRange(const std::map& map, Range valueRange) + { + for (auto& range : map) + { + if (valueRange.start >= range.first && valueRange.start < range.first + range.second.length) + { + return Range{range.second.start + valueRange.start - range.first, + range.second.length - (valueRange.start - range.first)}; + } + } + int64_t length = 0; + for (auto& range : map) + { + if (valueRange.start < range.first) + { + length = range.first - valueRange.start; + break; + } + } + if (length == 0) + length = valueRange.length; + + return Range{valueRange.start, length}; + } + + READ_INPUT(input) + { + std::string str; + Plantation plantation; + + std::getline(input, str); + std::stringstream ss{str}; + int64_t i; + ss >> "seeds: "; + while (ss >> i) plantation.seeds.emplace_back(i); + std::getline(input, str); + while (std::getline(input, str)) + { + std::map ranges; + std::getline(input, str); + while (!str.empty()) + { + int64_t source, destination, range; + std::stringstream ss{str}; + ss >> destination >> source >> range; + ranges.emplace(source, Range{destination, range}); + str = ""; + std::getline(input, str); + } + plantation.maps.emplace_back(ranges); + } + + return plantation; + } + + OUTPUT1(input) + { + int64_t min = FindLocation(input, input.seeds.front()); + for (auto seed : input.seeds) + { + min = std::min(min, FindLocation(input, seed)); + } + return min; + } + + void GetRanges(Range range, const std::map& toRanges, std::vector& resRanges) + { + while (true) + { + Range newRange = FindRange(toRanges, range); + if (range.length <= newRange.length) + { + newRange.length = range.length; + resRanges.emplace_back(newRange); + break; + } + resRanges.emplace_back(newRange); + range.start += newRange.length; + range.length -= newRange.length; + } + } + + OUTPUT2(input) + { + std::vector ranges; + for (int i = 0; i < input.seeds.size(); i += 2) + { + ranges.emplace_back(Range{input.seeds[i], input.seeds[i + 1]}); + } + for (auto map : input.maps) + { + std::vector newRanges; + for (auto& range : ranges) + { + GetRanges(range, map, newRanges); + } + ranges = newRanges; + } + + int64_t min = ranges.front().start; + for (auto range : ranges) + { + min = std::min(min, range.start); + } + return min; + } +} diff --git a/src/2023/Day06.cpp b/src/2023/Day06.cpp new file mode 100644 index 0000000..eafd7a5 --- /dev/null +++ b/src/2023/Day06.cpp @@ -0,0 +1,69 @@ +#include "common/aoc.h" + +namespace y2023::day06 +{ + using Game = std::pair; + REGISTER_DAY(2023, Day06, std::vector, int); + + REGISTER_TEST_EXAMPLE(2023, Day06, ExampleInput, 1, 288); + REGISTER_TEST(2023, Day06, Input, 1, 4811940); + REGISTER_TEST_EXAMPLE(2023, Day06, ExampleInput, 2, 71503); + REGISTER_TEST(2023, Day06, Input, 2, 30077773); + + READ_INPUT(input) + { + std::string str; + std::vector> games; + std::getline(input, str); + std::stringstream ss{str}; + int i; + ss >> "Time: "; + while (ss >> i) games.emplace_back(i, 0); + + std::getline(input, str); + std::stringstream ss2{str}; + ss2 >> "Distance: "; + + int count = 0; + while (ss2 >> i) + { + games[count].second = i; + count++; + } + return games; + } + + OUTPUT1(input) + { + int product = 1; + for (auto& [time, distance] : input) + { + int lowerBound = std::floor(time / 2.0 - std::sqrt(time * time / 4.0 - distance)) + 1; + int upperBound = std::ceil(time / 2.0 + sqrt(time * time / 4.0 - distance)) - 1; + + product *= upperBound - lowerBound + 1; + } + return product; + } + + OUTPUT2(input) + { + int64_t time = 0; + int64_t distance = 0; + std::string sTime = ""; + std::string sDistance = ""; + for (auto& game : input) + { + sTime += std::to_string(game.first); + sDistance += std::to_string(game.second); + } + char* endPtr; + time = std::strtoll(sTime.c_str(), &endPtr, 10); + distance = std::strtoll(sDistance.c_str(), &endPtr, 10); + + int lowerBound = std::floor(time / 2.0 - sqrt(time * time / 4.0 - distance)) + 1; + int upperBound = std::ceil(time / 2.0 + sqrt(time * time / 4.0 - distance)) - 1; + + return upperBound - lowerBound + 1; + } +} diff --git a/src/2023/Day07.cpp b/src/2023/Day07.cpp new file mode 100644 index 0000000..8299e40 --- /dev/null +++ b/src/2023/Day07.cpp @@ -0,0 +1,155 @@ +#include "common/aoc.h" + +namespace y2023::day07 +{ + int CalcStrength(const std::string& hand) + { + std::map score; + for (char c : hand) + { + score[c]++; + } + if (score.size() == 1) + return 7; // Five of a kind + else if (score.size() == 2) + { + if (score.begin()->second == 4 || score.begin()->second == 1) + return 6; // Four of a kind + else + return 5; // Full house + } + else if (score.size() == 3) + { + if (score.begin()->second == 2 || (++score.begin())->second == 2) + return 3; // Two pairs + else + return 4; // Three of a kind + } + else if (score.size() == 4) + return 2; // One pair + else + return 1; // High card + } + + struct Hand + { + std::string hand; + int bet; + + int strength = -1; + + void JokerStrength() + { + std::string replaces = "AKQT987654321"; + int maxStrength = 0; + char maxC; + for (auto c : replaces) + { + std::string newHand = hand; + std::replace(newHand.begin(), newHand.end(), 'J', c); + int strength = CalcStrength(newHand); + if (strength > maxStrength) + { + maxStrength = strength; + maxC = c; + } + } + + std::string newHand = hand; + std::replace(newHand.begin(), newHand.end(), 'J', maxC); + + strength = CalcStrength(newHand); + std::replace(hand.begin(), hand.end(), 'J', '0'); + } + + int GetValue(char c) const + { + if (Helper::IsDigit(c)) + return c - '0'; + else if (c == 'T') + return 10; + else if (c == 'J') + return 11; + else if (c == 'Q') + return 12; + else if (c == 'K') + return 13; + else if (c == 'A') + return 14; + std::cout << "Invalid card value" << std::endl; + return 0; + } + + bool operator<(const Hand& rhs) const + { + if (strength != rhs.strength) + return strength < rhs.strength; + + for (int i = 0; i < hand.size(); i++) + { + if (hand[i] != rhs.hand[i]) + { + return GetValue(hand[i]) < GetValue(rhs.hand[i]); + } + } + std::cout << "Same strength is invalid" << std::endl; + return false; + } + }; + + REGISTER_DAY(2023, Day07, std::vector, int); + + REGISTER_TEST_EXAMPLE(2023, Day07, ExampleInput, 1, 6440); + REGISTER_TEST(2023, Day07, Input, 1, 249390788); + REGISTER_TEST_EXAMPLE(2023, Day07, ExampleInput, 2, 5905); + REGISTER_TEST(2023, Day07, Input, 2, 248750248); + + READ_INPUT(input) + { + std::string str; + std::vector hands; + while (std::getline(input, str)) + { + std::stringstream ss{str}; + Hand hand; + ss >> hand.hand >> hand.bet; + hands.emplace_back(hand); + } + return hands; + } + + OUTPUT1(input) + { + std::vector cpy = input; + for (int i = 0; i < cpy.size(); i++) + { + cpy[i].strength = CalcStrength(cpy[i].hand); + } + std::sort(cpy.begin(), cpy.end()); + + int sum = 0; + for (int i = 0; i < cpy.size(); i++) + { + sum += cpy[i].bet * (i + 1); + } + + return sum; + } + + OUTPUT2(input) + { + std::vector cpy = input; + for (int i = 0; i < cpy.size(); i++) + { + cpy[i].JokerStrength(); + } + std::sort(cpy.begin(), cpy.end()); + + int sum = 0; + for (int i = 0; i < cpy.size(); i++) + { + sum += cpy[i].bet * (i + 1); + } + return sum; + } +} diff --git a/src/2023/Day08.cpp b/src/2023/Day08.cpp new file mode 100644 index 0000000..db60774 --- /dev/null +++ b/src/2023/Day08.cpp @@ -0,0 +1,83 @@ +#include "common/aoc.h" + +namespace y2023::day08 +{ + struct Map + { + std::string instruction; + std::string start; + std::map> map; + }; + + REGISTER_DAY(2023, Day08, Map, int64_t); + + REGISTER_TEST_EXAMPLE(2023, Day08, ExampleInput, 1, 2); + REGISTER_TEST(2023, Day08, Input, 1, 19099); + REGISTER_TEST_EXAMPLE(2023, Day08, ExampleInput, 2, 6); + REGISTER_TEST(2023, Day08, Input, 2, 17099847107071); + + READ_INPUT(input) + { + std::string str; + std::vector ints; + Map map; + std::getline(input, map.instruction); + std::getline(input, str); // empty line + while (std::getline(input, str)) + { + std::string s1, s2, s3; + std::stringstream ss{str}; + ss >> s1 >> "=" >> "(" >> s2 >> s3 >> ")"; + s2 = s2.substr(0, s2.size() - 1); + s3 = s3.substr(0, s3.size() - 1); + map.map.emplace(s1, std::make_pair(s2, s3)); + if (map.start.empty()) + map.start = s1; + } + return map; + } + + OUTPUT1(input) + { + std::string currentPos = "AAA"; + int i = 0; + while (currentPos != "ZZZ") + { + if (input.instruction[i % input.instruction.size()] == 'R') + currentPos = input.map.at(currentPos).second; + else + currentPos = input.map.at(currentPos).first; + i++; + } + return i; + } + + OUTPUT2(input) + { + std::map steps; + for (auto& [start, nodes] : input.map) + { + if (start[2] == 'A') + { + std::string currentPos = start; + int i = 0; + while (currentPos[2] != 'Z') + { + if (input.instruction[i % input.instruction.size()] == 'R') + currentPos = input.map.at(currentPos).second; + else + currentPos = input.map.at(currentPos).first; + i++; + } + steps[start] = i; + } + } + int64_t lcm = 1; + for (auto& [start, step] : steps) + { + lcm = std::lcm(lcm, step); + } + + return lcm; + } +} diff --git a/src/2023/Day09.cpp b/src/2023/Day09.cpp new file mode 100644 index 0000000..ea23213 --- /dev/null +++ b/src/2023/Day09.cpp @@ -0,0 +1,83 @@ +#include "common/aoc.h" + +namespace y2023::day09 +{ + REGISTER_DAY(2023, Day09, std::vector>, int); + + REGISTER_TEST_EXAMPLE(2023, Day09, ExampleInput, 1, 114); + REGISTER_TEST(2023, Day09, Input, 1, 1637452029); + REGISTER_TEST_EXAMPLE(2023, Day09, ExampleInput, 2, 2); + REGISTER_TEST(2023, Day09, Input, 2, 908); + + READ_INPUT(input) + { + std::string str; + std::vector> ints; + while (std::getline(input, str)) + { + std::vector numbers; + std::stringstream ss{str}; + int i; + while (ss >> i) numbers.emplace_back(i); + + ints.emplace_back(numbers); + } + return ints; + } + + bool AllZeros(const std::vector& numbers) + { + for (auto& number : numbers) + { + if (number != 0) + return false; + } + return true; + } + + std::vector GetDifference(const std::vector& numbers) + { + std::vector newNumbers{}; + for (int i = 0; i < numbers.size() - 1; i++) + { + newNumbers.emplace_back(numbers[i + 1] - numbers[i]); + } + return newNumbers; + } + + int GetExtrapolateForward(const std::vector& numbers) + { + if (AllZeros(numbers)) + return 0; + + return numbers.back() + GetExtrapolateForward(GetDifference(numbers)); + } + + int GetExtrapolateBackward(const std::vector& numbers) + { + if (AllZeros(numbers)) + return 0; + + return numbers.front() - GetExtrapolateBackward(GetDifference(numbers)); + } + + OUTPUT1(input) + { + int sum = 0; + for (auto& numbers : input) + { + sum += GetExtrapolateForward(numbers); + } + return sum; + } + + OUTPUT2(input) + { + int sum = 0; + for (auto& numbers : input) + { + sum += GetExtrapolateBackward(numbers); + } + return sum; + } +} diff --git a/src/2023/Day10.cpp b/src/2023/Day10.cpp new file mode 100644 index 0000000..f1f0438 --- /dev/null +++ b/src/2023/Day10.cpp @@ -0,0 +1,195 @@ +#include "common/aoc.h" + +namespace y2023::day10 +{ + REGISTER_DAY(2023, Day10, Array2D, int); + + REGISTER_TEST_EXAMPLE(2023, Day10, ExampleInput, 1, 70); + REGISTER_TEST(2023, Day10, Input, 1, 6682); + REGISTER_TEST_EXAMPLE(2023, Day10, ExampleInput, 2, 8); + REGISTER_TEST(2023, Day10, Input, 2, 353); + + READ_INPUT(input) + { + return Input::ReadArray2D(input); + } + + enum class Direction + { + North, + South, + East, + West + }; + + Index2D GetNextPosition(Index2D position, Direction direction) + { + switch (direction) + { + case Direction::North: + return Index2D{position.x, position.y - 1}; + case Direction::South: + return Index2D{position.x, position.y + 1}; + case Direction::East: + return Index2D{position.x + 1, position.y}; + case Direction::West: + return Index2D{position.x - 1, position.y}; + } + } + + Direction GetNextDirection(const Array2D& map, Index2D position, Direction prevDirection) + { + switch (prevDirection) + { + case Direction::North: + { + char c = map.Get(position.x, position.y - 1); + if (c == '|') + return Direction::North; + else if (c == 'F') + return Direction::East; + else if (c == '7') + return Direction::West; + } + case Direction::South: + { + char c = map.Get(position.x, position.y + 1); + if (c == '|') + return Direction::South; + else if (c == 'L') + return Direction::East; + else if (c == 'J') + return Direction::West; + } + case Direction::East: + { + char c = map.Get(position.x + 1, position.y); + if (c == '-') + return Direction::East; + else if (c == 'J') + return Direction::North; + else if (c == '7') + return Direction::South; + } + case Direction::West: + { + char c = map.Get(position.x - 1, position.y); + if (c == '-') + return Direction::West; + else if (c == 'F') + return Direction::South; + else if (c == 'L') + return Direction::North; + } + } + std::cerr << "Invalid direction" << std::endl; + return Direction::North; + } + + std::vector GetPath(const Array2D& map) + { + Index2D pos; + for (int y = 0; y < map.height; y++) + { + for (int x = 0; x < map.width; x++) + { + if (map.Get(x, y) == 'S') + pos = Index2D{x, y}; + } + } + + std::set south{'|', 'L', 'J'}; + std::set north{'|', 'F', '7'}; + std::set west{'-', 'F', 'L'}; + std::set east{'-', 'J', '7'}; + + std::vector surrounding; + Index2D cur; + Direction nextDirection; + if (pos.x > 0 && west.count(map.Get(pos.x - 1, pos.y))) + nextDirection = Direction::West; + else if (pos.y > 0 && north.count(map.Get(pos.x, pos.y - 1))) + nextDirection = Direction::North; + else if (pos.x < map.width - 1 && east.count(map.Get(pos.x + 1, pos.y))) + nextDirection = Direction::East; + else if (pos.y < map.height - 1 && south.count(map.Get(pos.x, pos.y + 1))) + nextDirection = Direction::South; + + std::vector path; + path.emplace_back(pos); + while (map.Get(GetNextPosition(pos, nextDirection)) != 'S') + { + Index2D prevPos = pos; + pos = GetNextPosition(pos, nextDirection); + path.emplace_back(pos); + nextDirection = GetNextDirection(map, prevPos, nextDirection); + } + return path; + } + + void RemoveFill(Array2D& map, Index2D pos) + { + std::stack stack; + stack.emplace(pos); + while (!stack.empty()) + { + Index2D top = stack.top(); + stack.pop(); + if (map.Get(top) == '.') + { + map.Set(top.x, top.y, ' '); + + for (auto neighbor : map.GetNeighbors(top, false)) + { + if (map.Get(neighbor) == '.') + stack.emplace(neighbor); + } + } + } + } + + OUTPUT1(input) + { + return GetPath(input).size() / 2; + } + + OUTPUT2(input) + { + std::vector path = GetPath(input); + Array2D newMap{input.width, input.height, '.'}; + + for (int i = 0; i < path.size(); i++) + { + newMap.Set(path[i].x, path[i].y, input.Get(path[i])); + } + + int sum = 0; + for (int y = 0; y < newMap.height; y++) + { + for (int x = 0; x < newMap.width; x++) + { + if (newMap.Get(x, y) == '.') + { + int add = 0; + + for (int i = 0; i < x; i++) + { + if (newMap.Get(i, y) == 'F') + add++; + if (newMap.Get(i, y) == 'J') + add++; + if (newMap.Get(i, y) == '7') + add--; + if (newMap.Get(i, y) == 'L') + add--; + if (newMap.Get(i, y) == '|') + add += 2; + } + if (std::abs(add) / 2 % 2 == 1) + sum++; + } + } + } + return sum; + } +} diff --git a/src/2023/Day11.cpp b/src/2023/Day11.cpp new file mode 100644 index 0000000..50baba9 --- /dev/null +++ b/src/2023/Day11.cpp @@ -0,0 +1,95 @@ +#include "common/aoc.h" + +namespace y2023::day11 +{ + REGISTER_DAY(2023, Day11, Array2D, int64_t); + + REGISTER_TEST_EXAMPLE(2023, Day11, ExampleInput, 1, 374); + REGISTER_TEST(2023, Day11, Input, 1, 9947476); + REGISTER_TEST_EXAMPLE(2023, Day11, ExampleInput, 2, 82000210); + REGISTER_TEST(2023, Day11, Input, 2, 519939907614); + + READ_INPUT(input) + { + return Input::ReadArray2D(input); + } + + bool IsInbetween(int i1, int i2, int between) + { + int min = std::min(i1, i2); + int max = std::max(i1, i2); + return min < between && between < max; + } + + int64_t GetSumPairs(const Array2D& input, int expansion) + { + std::set vertEmpty; + std::set horiEmpty; + for (int y = 0; y < input.height; y++) + { + bool empty = true; + for (int x = 0; x < input.width; x++) + { + if (input.Get(x, y) == '#') + { + empty = false; + break; + } + } + if (empty) + horiEmpty.emplace(y); + } + for (int x = 0; x < input.width; x++) + { + bool empty = true; + for (int y = 0; y < input.height; y++) + { + if (input.Get(x, y) == '#') + { + empty = false; + break; + } + } + if (empty) + vertEmpty.emplace(x); + } + std::vector galaxies; + input.Each( + [&](const Array2D& map, Index2D index) + { + if (map.Get(index) == '#') + galaxies.emplace_back(index); + }); + + int64_t sum = 0; + for (int i = 0; i < galaxies.size(); i++) + { + for (int j = i + 1; j < galaxies.size(); j++) + { + sum += std::abs(galaxies[i].x - galaxies[j].x) + std::abs(galaxies[i].y - galaxies[j].y); + for (auto& empty : vertEmpty) + { + if (IsInbetween(galaxies[i].x, galaxies[j].x, empty)) + sum += expansion; + } + for (auto& empty : horiEmpty) + { + if (IsInbetween(galaxies[i].y, galaxies[j].y, empty)) + sum += expansion; + } + } + } + + return sum; + } + + OUTPUT1(input) + { + return GetSumPairs(input, 1); + } + + OUTPUT2(input) + { + return GetSumPairs(input, 999999); + } +} diff --git a/src/2023/Day12.cpp b/src/2023/Day12.cpp new file mode 100644 index 0000000..77903fd --- /dev/null +++ b/src/2023/Day12.cpp @@ -0,0 +1,120 @@ +#include "common/aoc.h" + +namespace y2023::day12 +{ + struct Record + { + std::string str; + std::vector ranges; + }; + + // cache state + struct State + { + int recordIndex; + int rangeIndex; + int rangeSize; + + bool operator<(const State& rhs) const + { + if (recordIndex != rhs.recordIndex) + return recordIndex < rhs.recordIndex; + if (rangeIndex != rhs.rangeIndex) + return rangeIndex < rhs.rangeIndex; + return rangeSize < rhs.rangeSize; + } + }; + + REGISTER_DAY(2023, Day12, std::vector, int64_t); + + REGISTER_TEST_EXAMPLE(2023, Day12, ExampleInput, 1, 21); + REGISTER_TEST(2023, Day12, Input, 1, 7344); + REGISTER_TEST_EXAMPLE(2023, Day12, ExampleInput, 2, 525152); + REGISTER_TEST(2023, Day12, Input, 2, 1088006519007); + + READ_INPUT(input) + { + std::vector records; + std::string str; + while (std::getline(input, str)) + { + Record record; + std::stringstream ss{str}; + ss >> record.str; + int i; + while (ss >> i) + { + record.ranges.emplace_back(i); + ss >> ","; + } + records.emplace_back(record); + } + return records; + } + + int64_t CountRecords( + const Record& record, int recordIndex, int rangeIndex, int rangeSize, std::map& cache) + { + if (recordIndex == record.str.size()) + { + if (rangeIndex == record.ranges.size() && rangeSize == 0) + return 1; + else if (rangeIndex == record.ranges.size() - 1 && rangeSize == record.ranges.back()) + return 1; + else + return 0; + } + + auto it = cache.find(State{recordIndex, rangeIndex, rangeSize}); + if (it != cache.end()) + { + return it->second; + } + + int64_t sum = 0; + if (record.str[recordIndex] == '#' || record.str[recordIndex] == '?') + { + sum += CountRecords(record, recordIndex + 1, rangeIndex, rangeSize + 1, cache); + } + if (record.str[recordIndex] == '.' || record.str[recordIndex] == '?') + { + if (rangeSize == 0) + sum += CountRecords(record, recordIndex + 1, rangeIndex, rangeSize, cache); + else if (rangeIndex < record.ranges.size() && rangeSize == record.ranges[rangeIndex]) + sum += CountRecords(record, recordIndex + 1, rangeIndex + 1, 0, cache); + } + + cache.emplace(State{recordIndex, rangeIndex, rangeSize}, sum); + return sum; + } + + int64_t CountRecords(const Record& record) + { + std::map cache; + return CountRecords(record, 0, 0, 0, cache); + } + + OUTPUT1(input) + { + int64_t sum = 0; + for (auto& record : input) + { + sum += CountRecords(record); + } + return sum; + } + + OUTPUT2(input) + { + int64_t sum = 0; + for (auto& record : input) + { + Record newRecord; + newRecord.str = Helper::Repeat(record.str + '?', 5); + newRecord.str.pop_back(); + newRecord.ranges = Helper::Repeat(record.ranges, 5); + sum += CountRecords(newRecord); + } + return sum; + } +} diff --git a/src/2023/Day13.cpp b/src/2023/Day13.cpp new file mode 100644 index 0000000..59a431d --- /dev/null +++ b/src/2023/Day13.cpp @@ -0,0 +1,137 @@ +#include "common/aoc.h" + +namespace y2023::day13 +{ + REGISTER_DAY(2023, Day13, std::vector>, int); + + REGISTER_TEST_EXAMPLE(2023, Day13, ExampleInput, 1, 405); + REGISTER_TEST(2023, Day13, Input, 1, 36448); + REGISTER_TEST_EXAMPLE(2023, Day13, ExampleInput, 2, 400); + REGISTER_TEST(2023, Day13, Input, 2, 35799); + + READ_INPUT(input) + { + std::string str; + std::vector> maps; + + Array2D array = Input::ReadArray2D(input); + while (array.width != 0) + { + maps.emplace_back(array); + array = Input::ReadArray2D(input); + } + return maps; + } + + bool IsRowRecleted(const Array2D& map, int x, int y) + { + for (int i = 0; i < map.width; i++) + { + if (!map.IsInside(x + i + 1, y) || !map.IsInside(x - i, y)) + break; + if (map.Get(x + i + 1, y) != map.Get(x - i, y)) + return false; + } + return true; + } + + bool IsColumnRecleted(const Array2D& map, int x, int y) + { + for (int i = 0; i < map.height; i++) + { + if (!map.IsInside(x, y + i + 1) || !map.IsInside(x, y - i)) + break; + if (map.Get(x, y + i + 1) != map.Get(x, y - i)) + return false; + } + return true; + } + + bool IsReflectedVertical(const Array2D& map, int x) + { + for (int i = 0; i < map.height; i++) + { + if (!IsRowRecleted(map, x, i)) + return false; + } + return true; + } + + bool IsReflectedHorizontal(const Array2D& map, int y) + { + for (int i = 0; i < map.width; i++) + { + if (!IsColumnRecleted(map, i, y)) + return false; + } + return true; + } + + Index2D GetReflection(const Array2D& map, Index2D notIndex) + { + for (int x = 0; x < map.width - 1; x++) + { + if (IsReflectedVertical(map, x) && Index2D{x + 1, 0} != notIndex) + { + return Index2D{x + 1, 0}; + } + } + for (int y = 0; y < map.height - 1; y++) + { + if (IsReflectedHorizontal(map, y) && Index2D{0, y + 1} != notIndex) + { + return Index2D{0, y + 1}; + } + } + return Index2D{0, 0}; + } + + Index2D GetDifferentReflection(Array2D map) + { + Index2D reflection = GetReflection(map, Index2D{0, 0}); + + for (int y = 0; y < map.height; y++) + { + for (int x = 0; x < map.width; x++) + { + char c = map.Get(x, y); + if (map.Get(x, y) == '#') + map.Set(x, y, '.'); + else + map.Set(x, y, '#'); + + Index2D reflection2 = GetReflection(map, reflection); + if (reflection2 != Index2D{0, 0} && reflection2 != reflection) + return reflection2; + + map.Set(x, y, c); + } + } + std::cout << "here" << std::endl; + } + + // Too low: 35441 + OUTPUT1(input) + { + int i = 0; + int64_t sum = 0; + for (auto& array : input) + { + Index2D reflection = GetReflection(array, Index2D{0, 0}); + sum += reflection.x + reflection.y * 100; + } + return sum; + } + + OUTPUT2(input) + { + int i = 0; + int64_t sum = 0; + for (auto& array : input) + { + Index2D reflection = GetDifferentReflection(array); + sum += reflection.x + reflection.y * 100; + } + return sum; + } +} diff --git a/src/2023/Day14.cpp b/src/2023/Day14.cpp new file mode 100644 index 0000000..0922cbd --- /dev/null +++ b/src/2023/Day14.cpp @@ -0,0 +1,161 @@ +#include "common/aoc.h" + +namespace y2023::day14 +{ + REGISTER_DAY(2023, Day14, Array2D, int); + + REGISTER_TEST_EXAMPLE(2023, Day14, ExampleInput, 1, 136); + REGISTER_TEST(2023, Day14, Input, 1, 113525); + REGISTER_TEST_EXAMPLE(2023, Day14, ExampleInput, 2, 64); + REGISTER_TEST(2023, Day14, Input, 2, 101292); + + READ_INPUT(input) + { + return Input::ReadArray2D(input); + } + + void North(Array2D& map) + { + for (int y = 0; y < map.height; y++) + { + for (int x = 0; x < map.width; x++) + { + if (map.Get(x, y) == 'O') + { + map.Set(x, y, '.'); + int i = y; + while (i >= 0 && map.Get(x, i) == '.') + { + i--; + } + i++; + map.Set(x, i, 'O'); + } + } + } + } + + void South(Array2D& map) + { + for (int y = map.height - 1; y >= 0; y--) + { + for (int x = 0; x < map.width; x++) + { + if (map.Get(x, y) == 'O') + { + map.Set(x, y, '.'); + int i = y; + while (i < map.height && map.Get(x, i) == '.') + { + i++; + } + i--; + map.Set(x, i, 'O'); + } + } + } + } + + void West(Array2D& map) + { + for (int y = 0; y < map.height; y++) + { + for (int x = 0; x < map.width; x++) + { + if (map.Get(x, y) == 'O') + { + map.Set(x, y, '.'); + int i = x; + while (i >= 0 && map.Get(i, y) == '.') + { + i--; + } + i++; + map.Set(i, y, 'O'); + } + } + } + } + + void East(Array2D& map) + { + for (int y = 0; y < map.height; y++) + { + for (int x = map.width - 1; x >= 0; x--) + { + if (map.Get(x, y) == 'O') + { + map.Set(x, y, '.'); + int i = x; + while (i < map.width && map.Get(i, y) == '.') + { + i++; + } + i--; + map.Set(i, y, 'O'); + } + } + } + } + + int CalculateLoad(const Array2D& map) + { + int load = 0; + for (int y = 0; y < map.height; y++) + { + for (int x = 0; x < map.width; x++) + { + if (map.Get(x, y) == 'O') + load += (map.height - y); + } + } + return load; + } + + OUTPUT1(input) + { + Array2D map = input; + North(map); + return CalculateLoad(map); + } + + OUTPUT2(input) + { + Array2D map = input; + std::vector loads; + for (int i = 0; i < 1000000000; i++) + { + North(map); + West(map); + South(map); + East(map); + int load = CalculateLoad(map); + loads.emplace_back(load); + + for (int i = loads.size() - 2; i >= 0; i--) + { + if (loads[i] == load) + { + int loopSize = loads.size() - i - 1; + bool hasLoop = true; + for (int j = 0; j < loopSize; j++) + { + int k = loads.size() - j - 1; + // Check if the same load has occured in 3 loops. + if (k - loopSize * 3 < 0 || loads[k - loopSize * 3] != loads[k] || loads[k - loopSize * 2] != loads[k] || + loads[k - loopSize * 1] != loads[k]) + { + hasLoop = false; + } + } + if (hasLoop) + { + int loopOffset = (1000000000 - loads.size()) % loopSize; + return loads[loads.size() - 1 - loopSize + loopOffset]; + } + } + } + } + return 0; + } +} diff --git a/src/2023/Day15.cpp b/src/2023/Day15.cpp new file mode 100644 index 0000000..60f0cc5 --- /dev/null +++ b/src/2023/Day15.cpp @@ -0,0 +1,89 @@ +#include "common/aoc.h" + +namespace y2023::day15 +{ + REGISTER_DAY(2023, Day15, std::vector, int); + + REGISTER_TEST_EXAMPLE(2023, Day15, ExampleInput, 1, 1320); + REGISTER_TEST(2023, Day15, Input, 1, 497373); + REGISTER_TEST_EXAMPLE(2023, Day15, ExampleInput, 2, 145); + REGISTER_TEST(2023, Day15, Input, 2, 259356); + + READ_INPUT(input) + { + std::string str; + std::vector strs; + while (std::getline(input, str, ',')) + { + strs.emplace_back(str); + } + return strs; + } + + OUTPUT1(input) + { + int sum = 0; + for (auto& str : input) + { + int code = 0; + for (auto c : str) + { + code += c; + code *= 17; + code %= 256; + } + sum += code; + } + return sum; + } + + OUTPUT2(input) + { + std::vector>> boxes; + boxes.resize(256); + + for (auto& str : input) + { + int boxNr = 0; + int index = 0; + for (auto c : str) + { + if (c == '=' || c == '-') + break; + + boxNr += c; + boxNr *= 17; + boxNr %= 256; + index++; + } + + std::string box = str.substr(0, index); + auto it = std::find_if(boxes[boxNr].begin(), + boxes[boxNr].end(), + [&](const std::pair& elem) { return elem.first == box; }); + + if (str[index] == '=') + { + int focalLength = std::stoi(str.substr(index + 1)); + if (it != boxes[boxNr].end()) + it->second = focalLength; + else + boxes[boxNr].emplace_back(std::make_pair(box, focalLength)); + } + else + { + if (it != boxes[boxNr].end()) + boxes[boxNr].erase(it); + } + } + int sum = 0; + for (int i = 0; i < boxes.size(); i++) + { + for (int j = 0; j < boxes[i].size(); j++) + { + sum += (i + 1) * (j + 1) * boxes[i][j].second; + } + } + return sum; + } +} diff --git a/src/2023/Day16.cpp b/src/2023/Day16.cpp new file mode 100644 index 0000000..f8e170a --- /dev/null +++ b/src/2023/Day16.cpp @@ -0,0 +1,117 @@ +#include "common/aoc.h" + +namespace y2023::day16 +{ + struct Ray + { + Index2D pos; + Index2D vel; + }; + + REGISTER_DAY(2023, Day16, Array2D, int); + + REGISTER_TEST_EXAMPLE(2023, Day16, ExampleInput, 1, 46); + REGISTER_TEST(2023, Day16, Input, 1, 6855); + REGISTER_TEST_EXAMPLE(2023, Day16, ExampleInput, 2, 51); + REGISTER_TEST(2023, Day16, Input, 2, 7513); + + int Energize(const Array2D& map, const Ray& ray) + { + Array2D energized{map.width, map.height, '.'}; + std::stack rays; + rays.emplace(ray); + std::map splitters; + while (!rays.empty()) + { + Ray ray = rays.top(); + rays.pop(); + while (true) + { + ray.pos = ray.pos + ray.vel; + if (!map.IsInside(ray.pos)) + break; + energized.Set(ray.pos.x, ray.pos.y, '#'); + + if (map.Get(ray.pos) == '/') + { + if (ray.vel.x < 0) + ray.vel = Index2D{0, 1}; + else if (ray.vel.x > 0) + ray.vel = Index2D{0, -1}; + else if (ray.vel.y < 0) + ray.vel = Index2D{1, 0}; + else if (ray.vel.y > 0) + ray.vel = Index2D{-1, 0}; + } + else if (map.Get(ray.pos) == '\\') + { + if (ray.vel.x < 0) + ray.vel = Index2D{0, -1}; + else if (ray.vel.x > 0) + ray.vel = Index2D{0, 1}; + else if (ray.vel.y < 0) + ray.vel = Index2D{-1, 0}; + else if (ray.vel.y > 0) + ray.vel = Index2D{1, 0}; + } + else if (map.Get(ray.pos) == '-') + { + bool& hasSplit = splitters[ray.pos]; + if (ray.vel.y != 0 && !hasSplit) + { + rays.emplace(Ray{Index2D{ray.pos}, Index2D{-1, 0}}); + rays.emplace(Ray{Index2D{ray.pos}, Index2D{1, 0}}); + hasSplit = true; + break; + } + else if (hasSplit) + { + break; + } + } + else if (map.Get(ray.pos) == '|') + { + bool& hasSplit = splitters[ray.pos]; + if (ray.vel.x != 0 && !hasSplit) + { + rays.emplace(Ray{Index2D{ray.pos}, Index2D{0, -1}}); + rays.emplace(Ray{Index2D{ray.pos}, Index2D{0, 1}}); + hasSplit = true; + break; + } + else if (hasSplit) + { + break; + } + } + } + } + return energized.Count('#'); + } + + READ_INPUT(input) + { + return Input::ReadArray2D(input); + } + + OUTPUT1(input) + { + return Energize(input, Ray{Index2D{-1, 0}, Index2D{1, 0}}); + } + + OUTPUT2(input) + { + int max = 0; + for (int x = 0; x < input.width; x++) + { + max = std::max(max, Energize(input, Ray{Index2D{x, -1}, Index2D{0, 1}})); + max = std::max(max, Energize(input, Ray{Index2D{x, input.height}, Index2D{0, -1}})); + } + for (int y = 0; y < input.height; y++) + { + max = std::max(max, Energize(input, Ray{Index2D{-1, y}, Index2D{1, 0}})); + max = std::max(max, Energize(input, Ray{Index2D{input.width, y}, Index2D{-1, 0}})); + } + return max; + } +} diff --git a/src/2023/Day17.cpp b/src/2023/Day17.cpp new file mode 100644 index 0000000..7638dbd --- /dev/null +++ b/src/2023/Day17.cpp @@ -0,0 +1,103 @@ +#include "common/aoc.h" + +namespace y2023::day17 +{ + struct State + { + Index2D position; + Index2D direction; + int amount; + + bool operator<(const State& rhs) const + { + if (position != rhs.position) + return position < rhs.position; + if (direction != rhs.direction) + return direction < rhs.direction; + return amount < rhs.amount; + } + + friend std::ostream& operator<<(std::ostream& stream, const State& state) + { + return stream << state.position << " " << state.direction << " " << state.amount; + } + }; + + std::vector> Branch(const Array2D& map, const State& state) + { + std::vector> states; + if (state.amount != 3) + { + Index2D newPos = state.position + state.direction; + if (map.IsInside(newPos)) + states.emplace_back(map.Get(newPos), State{newPos, state.direction, state.amount + 1}); + } + + Index2D newPos1 = state.position + Index2D{state.direction.y, state.direction.x}; + if (map.IsInside(newPos1)) + states.emplace_back(map.Get(newPos1), State{newPos1, Index2D{state.direction.y, state.direction.x}, 1}); + + Index2D newPos2 = state.position + Index2D{-state.direction.y, -state.direction.x}; + if (map.IsInside(newPos2)) + states.emplace_back(map.Get(newPos2), State{newPos2, Index2D{-state.direction.y, -state.direction.x}, 1}); + return states; + } + + std::vector> Branch2(const Array2D& map, const State& state) + { + std::vector> states; + if (state.amount < 10) + { + Index2D newPos = state.position + state.direction; + if (map.IsInside(newPos)) + states.emplace_back(map.Get(newPos), State{newPos, state.direction, state.amount + 1}); + } + if (state.amount >= 4) + { + Index2D newPos1 = state.position + Index2D{state.direction.y, state.direction.x}; + if (map.IsInside(newPos1)) + states.emplace_back(map.Get(newPos1), State{newPos1, Index2D{state.direction.y, state.direction.x}, 1}); + + Index2D newPos2 = state.position + Index2D{-state.direction.y, -state.direction.x}; + if (map.IsInside(newPos2)) + states.emplace_back(map.Get(newPos2), State{newPos2, Index2D{-state.direction.y, -state.direction.x}, 1}); + } + return states; + } + + bool Goal(const Array2D& map, const State& state) + { + return state.position.x == map.width - 1 && state.position.y == map.height - 1; + } + + bool Goal2(const Array2D& map, const State& state) + { + return state.amount >= 4 && state.position.x == map.width - 1 && state.position.y == map.height - 1; + } + + REGISTER_DAY(2023, Day17, Array2D, int); + + REGISTER_TEST_EXAMPLE(2023, Day17, ExampleInput, 1, 102); + REGISTER_TEST(2023, Day17, Input, 1, 845); + REGISTER_TEST_EXAMPLE(2023, Day17, ExampleInput, 2, 94); + REGISTER_TEST(2023, Day17, Input, 2, 993); + + READ_INPUT(input) + { + return Input::ReadDigitsAsArray2D(input); + } + + OUTPUT1(input) + { + State startRight{Index2D{0, 0}, Index2D{1, 0}, 0}; + State startDown{Index2D{0, 0}, Index2D{0, 1}, 0}; + return Helper::Dijkstras(input, std::vector{startRight, startDown}, Branch, Goal); + } + + OUTPUT2(input) + { + State startRight{Index2D{0, 0}, Index2D{1, 0}, 0}; + State startDown{Index2D{0, 0}, Index2D{0, 1}, 0}; + return Helper::Dijkstras(input, std::vector{startRight, startDown}, Branch2, Goal2); + } +} diff --git a/src/2023/Day18.cpp b/src/2023/Day18.cpp new file mode 100644 index 0000000..f4206c6 --- /dev/null +++ b/src/2023/Day18.cpp @@ -0,0 +1,105 @@ +#include "common/aoc.h" + +namespace y2023::day18 +{ + struct Dig + { + char dir; + int amount; + std::string color; + }; + + REGISTER_DAY(2023, Day18, std::vector, int64_t); + + REGISTER_TEST_EXAMPLE(2023, Day18, ExampleInput, 1, 62); + REGISTER_TEST(2023, Day18, Input, 1, 35244); + REGISTER_TEST_EXAMPLE(2023, Day18, ExampleInput, 2, 952408144115); + REGISTER_TEST(2023, Day18, Input, 2, 85070763635666); + + READ_INPUT(input) + { + std::string str; + std::vector digs; + while (std::getline(input, str)) + { + Dig dig; + std::stringstream ss{str}; + ss >> dig.dir >> dig.amount >> "(#" >> dig.color; + dig.color.pop_back(); + digs.emplace_back(dig); + } + return digs; + } + + int64_t Area(const std::vector& digs) + { + Index2D pos{0, 0}; + std::vector indices; + for (auto& dig : digs) + { + if (dig.dir == 'R') + pos.x += dig.amount; + if (dig.dir == 'L') + pos.x -= dig.amount; + if (dig.dir == 'D') + pos.y += dig.amount; + if (dig.dir == 'U') + pos.y -= dig.amount; + indices.emplace_back(pos); + } + + int64_t area = 0; + for (int i = 0; i < digs.size() - 1; i++) + { + area += ((int64_t)indices[i].x * (int64_t)indices[i + 1].y) - ((int64_t)indices[i].y * (int64_t)indices[i + 1].x); + } + + area = std::abs(area / 2); + for (auto& dig : digs) + { + if (dig.dir == 'R') + area += dig.amount; + if (dig.dir == 'U') + area += dig.amount; + } + return area + 1; + } + + int HexToDec(const std::string& str) + { + int sum = 0; + for (char c : str) + { + if (Helper::IsDigit(c)) + sum = sum * 16 + (c - '0'); + else + sum = sum * 16 + (c - 'a' + 10); + } + return sum; + } + + OUTPUT1(input) + { + return Area(input); + } + + OUTPUT2(input) + { + std::vector digs = input; + for (auto& dig : digs) + { + int dec = HexToDec(dig.color); + dig.amount = dec / 16; + int dir = dec % 4; + if (dir == 0) + dig.dir = 'R'; + if (dir == 1) + dig.dir = 'D'; + if (dir == 2) + dig.dir = 'L'; + if (dir == 3) + dig.dir = 'U'; + } + return Area(digs); + } +} diff --git a/src/2023/Day19.cpp b/src/2023/Day19.cpp new file mode 100644 index 0000000..02636a5 --- /dev/null +++ b/src/2023/Day19.cpp @@ -0,0 +1,151 @@ +#include "common/aoc.h" + +namespace y2023::day19 +{ + struct Rule + { + char c; + bool lt; + int i; + std::string res; + }; + + struct Rules + { + std::vector rules; + std::string otherwise; + }; + + struct Instructions + { + std::map rules; + std::vector> data; + }; + + REGISTER_DAY(2023, Day19, Instructions, int64_t); + + REGISTER_TEST_EXAMPLE(2023, Day19, ExampleInput, 1, 19114); + REGISTER_TEST(2023, Day19, Input, 1, 532551); + REGISTER_TEST_EXAMPLE(2023, Day19, ExampleInput, 2, 167409079868000); + REGISTER_TEST(2023, Day19, Input, 2, 134343280273968); + + READ_INPUT(input) + { + std::string str; + Instructions instructions; + while (std::getline(input, str)) + { + if (str.empty()) + break; + std::stringstream ss{str}; + std::string state; + std::getline(ss, state, '{'); + char c; + Rules rules; + while (std::getline(ss, str, ',')) + { + if (str.back() == '}') + { + str.pop_back(); + rules.otherwise = str; + } + else + { + std::stringstream ss{str}; + Rule rule; + char lt; + ss >> rule.c >> lt >> rule.i >> ":" >> rule.res; + rule.lt = lt == '<'; + rules.rules.emplace_back(rule); + } + } + instructions.rules.emplace(state, rules); + } + + while (std::getline(input, str)) + { + std::stringstream ss{str}; + int x, m, a, s; + ss >> "{x=" >> x >> ",m=" >> m >> ",a=" >> a >> ",s=" >> s; + instructions.data.emplace_back(std::map{{'x', x}, {'m', m}, {'a', a}, {'s', s}}); + } + + return instructions; + } + + bool Accept(const Instructions& ins, const std::map& data, const std::string& workflow) + { + if (workflow == "A") + return true; + if (workflow == "R") + return false; + const Rules& rules = ins.rules.at(workflow); + for (auto& rule : rules.rules) + { + int i = data.at(rule.c); + if (rule.lt) + { + if (i < rule.i) + return Accept(ins, data, rule.res); + } + else + { + if (i > rule.i) + return Accept(ins, data, rule.res); + } + } + + return Accept(ins, data, rules.otherwise); + } + + int64_t Accept2(const Instructions& ins, std::map> data, const std::string& workflow) + { + if (workflow == "A") + { + return (int64_t)(data['x'].second - data['x'].first + 1) * (int64_t)(data['m'].second - data['m'].first + 1) * + (int64_t)(data['a'].second - data['a'].first + 1) * (int64_t)(data['s'].second - data['s'].first + 1); + } + if (workflow == "R") + return 0; + + int64_t sum = 0; + const Rules& rules = ins.rules.at(workflow); + for (auto& rule : rules.rules) + { + if (rule.lt) + { + std::map> newData = data; + newData[rule.c].second = rule.i - 1; + sum += Accept2(ins, newData, rule.res); + data[rule.c].first = rule.i; + } + else + { + std::map> newData = data; + newData[rule.c].first = rule.i + 1; + sum += Accept2(ins, newData, rule.res); + data[rule.c].second = rule.i; + } + } + + return sum + Accept2(ins, data, rules.otherwise); + } + + OUTPUT1(input) + { + int sum = 0; + for (auto& data : input.data) + { + if (Accept(input, data, "in")) + { + sum += data.at('x') + data.at('m') + data.at('a') + data.at('s'); + } + } + return sum; + } + + OUTPUT2(input) + { + return Accept2(input, {{'x', {1, 4000}}, {'m', {1, 4000}}, {'a', {1, 4000}}, {'s', {1, 4000}}}, "in"); + } +} diff --git a/src/2023/Day20.cpp b/src/2023/Day20.cpp new file mode 100644 index 0000000..2b76b08 --- /dev/null +++ b/src/2023/Day20.cpp @@ -0,0 +1,267 @@ +#include "common/aoc.h" + +namespace y2023::day20 +{ + struct Gate + { + char op; + std::string name; + + bool operator<(const Gate& rhs) const + { + return name < rhs.name; + } + }; + + using Gates = std::map>; + + REGISTER_DAY(2023, Day20, Gates, int64_t); + + REGISTER_TEST_EXAMPLE(2023, Day20, ExampleInput, 1, 11687500); + REGISTER_TEST(2023, Day20, Input, 1, 743090292); + // REGISTER_TEST_EXAMPLE(2023, Day20, ExampleInput, 2, 0); + REGISTER_TEST(2023, Day20, Input, 2, 241528184647003); + + READ_INPUT(input) + { + std::string str; + std::map> gates; + while (std::getline(input, str)) + { + std::stringstream ss{str}; + Gate gate; + ss >> gate.op >> gate.name >> "->"; + std::vector target; + while (std::getline(ss, str, ',')) + { + target.emplace_back(str.substr(1)); + } + gates.emplace(gate, target); + } + return gates; + } + + struct State + { + std::map flipFlopStates; + std::map> conjunctionStates; + }; + + struct Signal + { + std::string from; + std::string to; + bool high; + }; + + State InitiateState(const Gates& gates) + { + State state; + for (auto& gate : gates) + { + std::map states; + if (gate.first.op == '&') + { + for (auto& other : gates) + { + for (auto& target : other.second) + { + if (target == gate.first.name) + { + states.emplace(other.first.name, false); + } + } + } + state.conjunctionStates.emplace(gate.first.name, states); + } + } + return state; + } + + void HandleFlipFlop(const Signal& signal, + const std::vector& targets, + std::queue& queue, + State& state) + { + if (!signal.high) + { + state.flipFlopStates[signal.to] = !state.flipFlopStates[signal.to]; + for (auto& target : targets) + { + queue.emplace(Signal{signal.to, target, state.flipFlopStates[signal.to]}); + } + } + } + + bool HandleConjunction(const Signal& signal, + const std::vector& targets, + std::queue& queue, + State& state) + { + state.conjunctionStates[signal.to][signal.from] = signal.high; + bool sendHigh = false; + for (auto& states : state.conjunctionStates[signal.to]) + { + if (!states.second) + { + sendHigh = true; + break; + } + } + for (auto& target : targets) + { + queue.emplace(Signal{signal.to, target, sendHigh}); + } + return sendHigh; + } + + void HandleBroadcaster(const Signal& signal, const std::vector& targets, std::queue& queue) + { + for (auto& target : targets) + { + queue.emplace(Signal{signal.to, target, signal.high}); + } + } + + int64_t CalculateCommonStart(int64_t start1, int64_t loop1, int64_t start2, int64_t loop2) + { + while (start1 != start2) + { + if (start1 < start2) + { + int count = std::max(int64_t{1}, (start2 - start1) / loop1); + start1 += count * loop1; + } + else + { + int count = std::max(int64_t{1}, (start1 - start2) / loop2); + start2 += count * loop2; + } + } + return start1; + } + + int64_t CalculateSolution(const std::vector>& startLoops) + { + int64_t start1 = startLoops[0].first; + int64_t loop1 = startLoops[0].second; + for (int i = 1; i < startLoops.size(); i++) + { + start1 = CalculateCommonStart(start1, loop1, startLoops[i].first, startLoops[i].second); + loop1 = std::lcm(loop1, startLoops[i].second); + } + return start1; + } + + bool IsDone(const std::map>& gateLoops, const std::set& neededGates) + { + if (gateLoops.size() != neededGates.size()) + return false; + for (auto& loop : gateLoops) + { + if (loop.second.second == 0) + return false; + } + return true; + } + + std::set GetGatesToTarget(const Gates& gates, const std::string& target) + { + std::set sources; + for (auto& gate : gates) + { + for (auto& t : gate.second) + { + if (t == target) + sources.emplace(gate.first.name); + } + } + return sources; + } + + OUTPUT1(input) + { + State state = InitiateState(input); + + std::pair signals{0, 0}; + for (int i = 0; i < 1000; i++) + { + std::queue queue; + queue.emplace(Signal{"", "roadcaster", false}); + while (!queue.empty()) + { + Signal signal = queue.front(); + queue.pop(); + if (signal.high) + signals.first += 1; + else + signals.second += 1; + auto it = input.find(Gate{'x', signal.to}); + if (it == input.end()) + continue; + if (it->first.op == '%') + HandleFlipFlop(signal, it->second, queue, state); + else if (it->first.op == '&') + HandleConjunction(signal, it->second, queue, state); + else if (it->first.name == "roadcaster") + HandleBroadcaster(signal, it->second, queue); + } + } + return (int64_t)signals.first * (int64_t)signals.second; + } + + OUTPUT2(input) + { + State state = InitiateState(input); + + std::set gateToRx = GetGatesToTarget(input, "rx"); // will only be 1 source gate + std::set neededGates = GetGatesToTarget(input, *gateToRx.begin()); + + std::map> loops; + int i = 0; + while (true) + { + i++; + std::queue queue; + queue.emplace(Signal{"", "roadcaster", false}); + while (!queue.empty()) + { + Signal signal = queue.front(); + queue.pop(); + auto it = input.find(Gate{'x', signal.to}); + if (it == input.end()) + continue; + if (it->first.op == '%') + { + HandleFlipFlop(signal, it->second, queue, state); + } + else if (it->first.op == '&') + { + if (HandleConjunction(signal, it->second, queue, state)) + { + if (neededGates.count(signal.to)) + { + int loopSize = 0; + auto it = loops.find(signal.to); + if (it != loops.end()) + { + loopSize = (i - it->second.first); + } + loops[signal.to] = {i, loopSize}; + } + } + } + else if (it->first.name == "roadcaster") + HandleBroadcaster(signal, it->second, queue); + } + if (IsDone(loops, neededGates)) + break; + } + std::vector> result; + for (auto& loop : loops) + { + result.emplace_back(loop.second); + } + return CalculateSolution(result); + } +} diff --git a/src/2023/Day21.cpp b/src/2023/Day21.cpp new file mode 100644 index 0000000..b29c856 --- /dev/null +++ b/src/2023/Day21.cpp @@ -0,0 +1,98 @@ +#include "common/aoc.h" + +namespace y2023::day21 +{ + REGISTER_DAY(2023, Day21, Array2D, int64_t); + + REGISTER_TEST_EXAMPLE(2023, Day21, ExampleInput, 1, 47); + REGISTER_TEST(2023, Day21, Input, 1, 3651); + REGISTER_TEST_EXAMPLE(2023, Day21, ExampleInput, 2, 528192865877841); + REGISTER_TEST(2023, Day21, Input, 2, 607334325965751); + + READ_INPUT(input) + { + return Input::ReadArray2D(input); + } + + int64_t Visited(const Array2D& map, Index2D start, int maxSteps) + { + if (maxSteps < 0) + return 0; + std::set exactly; + std::set visited; + std::queue> stack{}; + stack.push(std::make_pair(start, 0)); + + while (!stack.empty()) + { + std::pair pos = stack.front(); + stack.pop(); + if (pos.second % 2 == maxSteps % 2) + { + exactly.emplace(pos.first); + } + if (pos.second == maxSteps) + continue; + + for (auto& i : map.GetNeighbors(pos.first, false)) + { + if (map.Get(i) != '#' && visited.emplace(i).second) + { + stack.push(std::make_pair(i, pos.second + 1)); + } + } + } + return exactly.size(); + } + + OUTPUT1(input) + { + Index2D pos = input.Find('S'); + return Visited(input, {pos}, 64); + } + + OUTPUT2(input) + { + int64_t steps = 26501365; + int64_t halfStep = input.width / 2 + 1; + int64_t boxes = steps / input.width - 1; // How many grids in any direction we will have with full traversal + + int64_t traversed = boxes * input.width + input.width / 2; + + int64_t totalOdd = (boxes / 2 + boxes / 2 + 1) * (boxes / 2 + boxes / 2 + 1); + int64_t totalEven = 4 * ((boxes + 1) / 2) * ((boxes + 1) / 2); + + int64_t stepsLeft = steps - traversed - 1; + + // Calculate the amount of positions we can get to if we traverse the entire grid + int64_t odds = Visited(input, {Index2D{input.width / 2, input.height / 2}}, 2 * input.width + steps); + int64_t evens = Visited(input, {Index2D{input.width / 2, input.height / 2 + 1}}, 2 * input.width + steps); + int64_t fullGridsTotal = odds * (int64_t)totalOdd + evens * (int64_t)totalEven; + + // Corner case handling + int64_t TL = Visited(input, Index2D{input.width - 1, input.height - 1}, stepsLeft + halfStep - 1); + int64_t TR = Visited(input, Index2D{0, input.height - 1}, stepsLeft + halfStep - 1); + int64_t BL = Visited(input, Index2D{input.width - 1, 0}, stepsLeft + halfStep - 1); + int64_t BR = Visited(input, Index2D{0, 0}, stepsLeft + halfStep - 1); + + int64_t TL2 = Visited(input, Index2D{input.width - 1, input.height - 1}, stepsLeft - halfStep); + int64_t TR2 = Visited(input, Index2D{0, input.height - 1}, stepsLeft - halfStep); + int64_t BL2 = Visited(input, Index2D{input.width - 1, 0}, stepsLeft - halfStep); + int64_t BR2 = Visited(input, Index2D{0, 0}, stepsLeft - halfStep); + int64_t cornerTotal = (TL + TR + BL + BR) * boxes + (TL2 + TR2 + BL2 + BR2) * (int64_t)(boxes + 1); + + // Endpoint handling + int64_t L = Visited(input, Index2D{input.width - 1, input.height / 2}, stepsLeft); + int64_t T = Visited(input, Index2D{input.width / 2, input.height - 1}, stepsLeft); + int64_t R = Visited(input, Index2D{0, input.height / 2}, stepsLeft); + int64_t B = Visited(input, Index2D{input.width / 2, 0}, stepsLeft); + + int64_t L2 = Visited(input, Index2D{input.width - 1, input.height / 2}, stepsLeft - input.width); + int64_t T2 = Visited(input, Index2D{input.width / 2, input.height - 1}, stepsLeft - input.width); + int64_t R2 = Visited(input, Index2D{0, input.height / 2}, stepsLeft - input.width); + int64_t B2 = Visited(input, Index2D{input.width / 2, 0}, stepsLeft - input.width); + int64_t edgeTotal = L + T + R + B + L2 + T2 + R2 + B2; + + return fullGridsTotal + cornerTotal + edgeTotal; + } +} diff --git a/src/2023/Day22.cpp b/src/2023/Day22.cpp new file mode 100644 index 0000000..1c1f3d0 --- /dev/null +++ b/src/2023/Day22.cpp @@ -0,0 +1,169 @@ +#include "common/aoc.h" + +namespace y2023::day22 +{ + struct Brick + { + Index3D pos; + Index3D pos2; + int index; + + friend std::ostream& operator<<(std::ostream& stream, const Brick& brick) + { + return stream << brick.index << ": " << brick.pos << " - " << brick.pos2; + } + }; + + REGISTER_DAY(2023, Day22, std::vector, int); + + REGISTER_TEST_EXAMPLE(2023, Day22, ExampleInput, 1, 5); + REGISTER_TEST(2023, Day22, Input, 1, 517); + REGISTER_TEST_EXAMPLE(2023, Day22, ExampleInput, 2, 7); + REGISTER_TEST(2023, Day22, Input, 2, 61276); + + READ_INPUT(input) + { + std::string str; + std::vector bricks; + int i = 0; + while (std::getline(input, str)) + { + std::stringstream ss{str}; + Brick brick; + ss >> brick.pos.x >> "," >> brick.pos.y >> "," >> brick.pos.z >> "~" >> brick.pos2.x >> "," >> brick.pos2.y >> + "," >> brick.pos2.z; + + // Make it so that pos is always the minimum position and pos2 is always the maximum position + Brick actualBrick; + actualBrick.pos.x = std::min(brick.pos.x, brick.pos2.x); + actualBrick.pos.y = std::min(brick.pos.y, brick.pos2.y); + actualBrick.pos.z = std::min(brick.pos.z, brick.pos2.z); + actualBrick.pos2.x = std::max(brick.pos.x, brick.pos2.x); + actualBrick.pos2.y = std::max(brick.pos.y, brick.pos2.y); + actualBrick.pos2.z = std::max(brick.pos.z, brick.pos2.z); + actualBrick.index = i; + i++; + bricks.emplace_back(actualBrick); + } + return bricks; + } + + int GetZ(const Brick& brick, const std::map& occupied) + { + int z = brick.pos.z; + while (z > 1) + { + for (int y = brick.pos.y; y <= brick.pos2.y; y++) + { + for (int x = brick.pos.x; x <= brick.pos2.x; x++) + { + auto it = occupied.find(Index3D{x, y, z - 1}); + if (it != occupied.end()) + { + return z; + } + } + } + z--; + } + return z; + } + + bool CanMove(const Brick& brick, const std::map& occupied, int ignore) + { + if (brick.pos.z == 1) + return false; + for (int y = brick.pos.y; y <= brick.pos2.y; y++) + { + for (int x = brick.pos.x; x <= brick.pos2.x; x++) + { + auto it = occupied.find(Index3D{x, y, brick.pos.z - 1}); + if (it != occupied.end() && it->second != brick.index && it->second != ignore) + { + return false; + } + } + } + return true; + } + + void Set(const Brick& brick, std::map& occupied) + { + for (int z = brick.pos.z; z <= brick.pos2.z; z++) + { + for (int y = brick.pos.y; y <= brick.pos2.y; y++) + { + for (int x = brick.pos.x; x <= brick.pos2.x; x++) + { + occupied.emplace(Index3D{x, y, z}, brick.index); + } + } + } + } + + bool CanRemoveBrick(int brickIndex, const std::vector& bricks, const std::map& occupied) + { + for (int i = 0; i < bricks.size(); i++) + { + if (CanMove(bricks[i], occupied, brickIndex)) + { + return false; + } + } + return true; + } + + void Settle(std::vector& bricks, std::map& occupied) + { + std::sort(bricks.begin(), bricks.end(), [](const Brick& lhs, const Brick& rhs) { return lhs.pos.z < rhs.pos.z; }); + for (int i = 0; i < bricks.size(); i++) + { + int z = GetZ(bricks[i], occupied); + bricks[i].pos2.z = z + (bricks[i].pos2.z - bricks[i].pos.z); + bricks[i].pos.z = z; + Set(bricks[i], occupied); + } + std::sort(bricks.begin(), bricks.end(), [](const Brick& lhs, const Brick& rhs) { return lhs.pos.z < rhs.pos.z; }); + } + + OUTPUT1(input) + { + std::map occupied; + std::vector cpy = input; + Settle(cpy, occupied); + int sum = 0; + for (int i = 0; i < input.size(); i++) + { + if (CanRemoveBrick(input[i].index, cpy, occupied)) + sum++; + } + return sum; + } + + OUTPUT2(input) + { + std::vector cpy = input; + std::map occupied; + Settle(cpy, occupied); + + int sum = 0; + for (int i = 0; i < cpy.size(); i++) + { + std::map occupied; + std::set handled; + std::vector cpy2 = cpy; + for (int j = 0; j < cpy.size(); j++) + { + if (j == i) + continue; + int z = GetZ(cpy2[j], occupied); + if (z != cpy2[j].pos.z) + sum++; + cpy2[j].pos2.z = z + (cpy[j].pos2.z - cpy[j].pos.z); + cpy2[j].pos.z = z; + Set(cpy2[j], occupied); + } + } + return sum; + } +} diff --git a/src/2023/Day23.cpp b/src/2023/Day23.cpp new file mode 100644 index 0000000..0ac432a --- /dev/null +++ b/src/2023/Day23.cpp @@ -0,0 +1,176 @@ +#include "common/aoc.h" + +namespace y2023::day23 +{ + REGISTER_DAY(2023, Day23, Array2D, int); + + REGISTER_TEST_EXAMPLE(2023, Day23, ExampleInput, 1, 94); + REGISTER_TEST(2023, Day23, Input, 1, 2130); + REGISTER_TEST_EXAMPLE(2023, Day23, ExampleInput, 2, 154); + REGISTER_TEST(2023, Day23, Input, 2, 6710); + + READ_INPUT(input) + { + return Input::ReadArray2D(input); + } + + struct State + { + Index2D pos; + int cost; + std::set visited; + }; + + struct State2 + { + Index2D pos; + int cost; + + Index2D lastIntersection; + int lastIntersectionCost; + std::set visited; + }; + + struct DP + { + std::set visited; + Index2D pos; + + bool operator<(const DP& rhs) const + { + if (pos != rhs.pos) + return pos < rhs.pos; + if (visited.size() != rhs.visited.size()) + return visited.size() < rhs.visited.size(); + + std::set::iterator it1 = visited.begin(); + std::set::iterator it2 = rhs.visited.begin(); + for (int i = 0; i < visited.size(); i++) + { + if (*it1 != *it2) + return *it1 < *it2; + it1++; + it2++; + } + return false; + } + }; + + std::set GetIntersections(const Array2D& map) + { + std::set intersections; + for (int y = 0; y < map.height; y++) + { + for (int x = 0; x < map.width; x++) + { + if (map.Get(x, y) != '.') + continue; + + int paths = 0; + for (auto& neighbor : map.GetNeighbors(Index2D{x, y}, false)) + { + if (map.Get(neighbor) != '#') + paths++; + } + if (paths != 2) + intersections.emplace(Index2D{x, y}); + } + } + return intersections; + } + + void FindNextIntersection1(const Array2D& map, + Graph& graph, + Index2D from, + const std::set& intersections) + { + for (auto& neighbor : map.GetNeighbors(from, false)) + { + if (map.Get(neighbor) == '#') + continue; + + std::set visited; + visited.emplace(from); + visited.emplace(neighbor); + + Index2D pos = neighbor; + int cost = 1; + bool canFind = true; + while (canFind && intersections.count(pos) == 0) + { + canFind = false; + for (auto& next : map.GetNeighbors(pos, false)) + { + if (!visited.emplace(next).second) + continue; + if (map.Get(next) == '.' || map.Get(next) == '<' && next.x < pos.x || + map.Get(next) == '>' && next.x > pos.x || map.Get(next) == 'v' && next.y > pos.y || + map.Get(next) == '^' && next.y < pos.y) + { + pos = next; + canFind = true; + break; + } + } + cost++; + } + if (canFind) + graph.AddOneWayEdge(from, pos, cost); + } + } + + void FindNextIntersection2(const Array2D& map, + Graph& graph, + Index2D from, + const std::set& intersections) + { + for (auto& neighbor : map.GetNeighbors(from, false)) + { + if (map.Get(neighbor) == '#') + continue; + + std::set visited; + visited.emplace(from); + visited.emplace(neighbor); + + Index2D pos = neighbor; + int cost = 1; + while (intersections.count(pos) == 0) + { + for (auto& neighbor : map.GetNeighbors(pos, false)) + { + if (map.Get(neighbor) != '#' && visited.emplace(neighbor).second) + { + pos = neighbor; + break; + } + } + cost++; + } + graph.AddOneWayEdge(from, pos, cost); + } + } + + OUTPUT1(input) + { + Graph graph; + std::set intersections = GetIntersections(input); + for (auto& intersection : intersections) + { + FindNextIntersection1(input, graph, intersection, intersections); + } + return graph.DFSLong(Index2D{1, 0}, Index2D{input.width - 2, input.height - 1}); + } + + OUTPUT2(input) + { + Graph graph; + std::set intersections = GetIntersections(input); + + for (auto& intersection : intersections) + { + FindNextIntersection2(input, graph, intersection, intersections); + } + return graph.DFSLong(Index2D{1, 0}, Index2D{input.width - 2, input.height - 1}); + } +} diff --git a/src/2023/Day24.cpp b/src/2023/Day24.cpp new file mode 100644 index 0000000..b1cd9e8 --- /dev/null +++ b/src/2023/Day24.cpp @@ -0,0 +1,284 @@ +#include "common/aoc.h" + +namespace y2023::day24 +{ + bool DoubleEq0(double d) + { + return std::abs(d) < 0.00000001; + } + + struct Vec3 + { + int64_t x = 0; + int64_t y = 0; + int64_t z = 0; + + static Vec3 Cross(const Vec3& lhs, const Vec3& rhs) + { + return Vec3{lhs.y * rhs.z - lhs.z * rhs.y, lhs.z * rhs.x - lhs.x * rhs.z, lhs.x * rhs.y - lhs.y * rhs.x}; + } + + Vec3 operator+(const Vec3& rhs) const + { + return Vec3{x + rhs.x, y + rhs.y, z + rhs.z}; + } + + Vec3 operator-(const Vec3& rhs) const + { + return Vec3{x - rhs.x, y - rhs.y, z - rhs.z}; + } + }; + + struct Vec6 + { + double data[6]; + + Vec6(const Vec3& vec1, const Vec3& vec2) + { + data[0] = vec1.x; + data[1] = vec1.y; + data[2] = vec1.z; + data[3] = vec2.x; + data[4] = vec2.y; + data[5] = vec2.z; + } + + Vec6(double i1, double i2, double i3, double i4, double i5, double i6) + { + data[0] = i1; + data[1] = i2; + data[2] = i3; + data[3] = i4; + data[4] = i5; + data[5] = i6; + } + }; + + struct Mat6 + { + double data[6][6]; + + Mat6() + { + std::memset(data, 0, 6 * 6 * sizeof(double)); + } + + void CrossMatrix(const Vec3& vec, int x, int y) + { + data[y][x] = 0; + data[y][x + 1] = vec.z; + data[y][x + 2] = -vec.y; + data[y + 1][x] = -vec.z; + data[y + 1][x + 1] = 0; + data[y + 1][x + 2] = vec.x; + data[y + 2][x] = vec.y; + data[y + 2][x + 1] = -vec.x; + data[y + 2][x + 2] = 0; + } + + void AddRows(Vec6& rhs, int rowTo, int rowFrom, double scale) + { + for (int i = 0; i < 6; i++) + { + data[rowTo][i] = data[rowTo][i] + data[rowFrom][i] * scale; + } + rhs.data[rowTo] = rhs.data[rowTo] + rhs.data[rowFrom] * scale; + } + + // Find row that can be added to row y to make (x, y) = 0 + // Should follow | 0 0 0 A B | where A is on column x and is not 0 + int FindRow(int x, int y) + { + for (int i = 0; i < 6; i++) + { + if (i != y && !DoubleEq0(data[i][x])) + { + bool found = true; + for (int j = 0; j < x; j++) + { + if (!DoubleEq0(data[i][j])) + { + found = false; + break; + } + } + if (found) + return i; + } + } + std::cerr << "Didn't find row" << std::endl; + return 0; + } + + void NormalizeRow(Vec6& rhs, int y, int x) + { + double div = data[y][x]; + for (int i = y; i < 6; i++) + { + data[y][i] /= div; + } + rhs.data[y] /= div; + } + + void EliminateDiagonal(Vec6& rhs, int d) + { + if (DoubleEq0(data[d][d])) + { + for (int i = d + 1; i < 6; i++) + { + if (!DoubleEq0(data[i][d])) + { + AddRows(rhs, d, i, 1); + break; + } + } + } + NormalizeRow(rhs, d, d); + } + + void EliminateOther(Vec6& rhs, int x, int y) + { + if (!DoubleEq0(data[y][x])) + { + int row = FindRow(x, y); + AddRows(rhs, y, row, -data[y][x] / data[row][x]); + } + } + + void Eliminate(Vec6& rhs, int x, int y) + { + if (x == y) + EliminateDiagonal(rhs, x); + else + EliminateOther(rhs, x, y); + } + + Vec6 GaussianElemination(Vec6 rhs) const + { + Mat6 mat = *this; + + for (int x = 0; x < 6; x++) + { + for (int y = 0; y < 6; y++) + { + mat.Eliminate(rhs, x, y); + } + } + return rhs; + } + }; + + struct Wind + { + Vec3 pos; + Vec3 vel; + }; + + REGISTER_DAY(2023, Day24, std::vector, int64_t); + + REGISTER_TEST_EXAMPLE(2023, Day24, ExampleInput, 1, 2); + REGISTER_TEST(2023, Day24, Input, 1, 18651); + REGISTER_TEST_EXAMPLE(2023, Day24, ExampleInput, 2, 47); + REGISTER_TEST(2023, Day24, Input, 2, 546494494317645); + + READ_INPUT(input) + { + std::string str; + std::vector winds; + while (std::getline(input, str)) + { + std::stringstream ss{str}; + Wind wind; + ss >> wind.pos.x >> "," >> wind.pos.y >> "," >> wind.pos.z >> "@" >> wind.vel.x >> "," >> wind.vel.y >> "," >> + wind.vel.z; + winds.emplace_back(wind); + } + return winds; + } + + bool GetCollisionsXY(const Wind& wind1, const Wind& wind2, int64_t min, int64_t max) + { + if (wind1.vel.x * wind2.vel.y == wind2.vel.x * wind1.vel.y) + return false; + + double t2 = (wind1.vel.y * (wind2.pos.x - wind1.pos.x) - wind1.vel.x * (wind2.pos.y - wind1.pos.y)) / + (double)(wind1.vel.x * wind2.vel.y - wind2.vel.x * wind1.vel.y); + double t1 = ((wind2.pos.x + wind2.vel.x * t2) - wind1.pos.x) / wind1.vel.x; + if (t1 < 0 || t2 < 0) + return false; + double x = wind1.pos.x + wind1.vel.x * t1; + double y = wind1.pos.y + wind1.vel.y * t1; + bool isCollision = x >= min && y >= min && x <= max && y <= max; + + return isCollision; + } + + OUTPUT1(input) + { + int64_t min = 7; + int64_t max = 27; + if (!isExample) + { + min = 200000000000000; + max = 400000000000000; + } + int sum = 0; + for (int i = 0; i < input.size(); i++) + { + for (int j = i + 1; j < input.size(); j++) + { + if (GetCollisionsXY(input[i], input[j], min, max)) + { + sum++; + } + } + } + return sum; + } + + OUTPUT2(input) + { +#if 0 + Derivation: + + p[i] + v[i] * t = p + v * t + (p - p[i]) = (v - v[i]) * -t // means the vector (p - p[i]) and (v - v[i]) are parallel + + (p - p[i]) x (v - v[i]) == 0 + p x v - p[i] x v - p x v[i] + p[i] x v[i] == 0 + p x v == p[i] x v + p x v[i] - p[i] x v[i] + + p[1] x v + p x v[1] - p[1] x v[1] == p[0] x v + p x v[0] - p[0] x v[0] + p[2] x v + p x v[2] - p[2] x v[2] == p[0] x v + p x v[0] - p[0] x v[0] + + p x (v[1] - v[0]) + (p[1] - p[0]) x v == p[1] x v[1] - p[0] x v[0] + p x (v[2] - v[0]) + (p[2] - p[0]) x v == p[2] x v[2] - p[0] x v[0] + + vij = v[i] - v[j] + pij = p[i] - p[j] + Cij = p[j] x v[j] - p[i] x v[i] + + p x v10 - p10 x v == C10; + p x v20 - p20 x v == C10; + + | 0 v10z -v10y 0 p10z -p10y | C10x | + | -v10z 0 v10x -p10z 0 p10x | C10y | + | v10y -v10x 0 p10y -p10x 0 | C10z | + | 0 v20z -v20y 0 p20z -p20y | C20x | + | -v20z 0 v20x -p20z 0 p20x | C20y | + | v20y -v20x 0 p20y -p20x 0 | C20z | +#endif + + Mat6 mat; + mat.CrossMatrix(input[1].vel - input[0].vel, 0, 0); + mat.CrossMatrix(input[2].vel - input[0].vel, 0, 3); + mat.CrossMatrix(input[1].pos - input[0].pos, 3, 0); + mat.CrossMatrix(input[2].pos - input[0].pos, 3, 3); + Vec3 C10 = Vec3::Cross(input[1].pos, input[1].vel) - Vec3::Cross(input[0].pos, input[0].vel); + Vec3 C20 = Vec3::Cross(input[2].pos, input[2].vel) - Vec3::Cross(input[0].pos, input[0].vel); + Vec6 rhs{C10, C20}; + Vec6 res = mat.GaussianElemination(rhs); + + return int64_t(res.data[0]) + int64_t(res.data[1]) + int64_t(res.data[2]); + } +} diff --git a/src/2023/Day25.cpp b/src/2023/Day25.cpp new file mode 100644 index 0000000..1b119d1 --- /dev/null +++ b/src/2023/Day25.cpp @@ -0,0 +1,117 @@ +#include "common/aoc.h" + +namespace y2023::day25 +{ + using Input = Graph; + REGISTER_DAY(2023, Day25, Input, int); + + REGISTER_TEST_EXAMPLE(2023, Day25, ExampleInput, 1, 54); + REGISTER_TEST(2023, Day25, Input, 1, 583632); + REGISTER_TEST_EXAMPLE(2023, Day25, ExampleInput, 2, 0); + REGISTER_TEST(2023, Day25, Input, 2, 0); + + READ_INPUT(input) + { + std::string str; + Graph graph; + while (std::getline(input, str)) + { + std::stringstream ss{str}; + std::string s; + ss >> s; + s.pop_back(); + std::string s2; + while (ss >> s2) + { + graph.AddTwoWayEdge(s, s2, 1); + } + } + return graph; + } + + std::pair FloodFill(const Graph& graph, + const std::set>& exclude) + { + std::set visited; + visited.emplace(*graph.GetNodes().begin()); + std::stack stack; + stack.emplace(*graph.GetNodes().begin()); + while (!stack.empty()) + { + std::string s = stack.top(); + stack.pop(); + + for (auto& [next, nextCost] : graph.GetPaths(s)) + { + if (exclude.count(std::make_pair(next, s)) || exclude.count(std::make_pair(s, next))) + continue; + if (visited.emplace(next).second) + stack.emplace(next); + } + } + return {visited.size() != graph.GetNodes().size(), visited.size()}; + } + + OUTPUT1(input) + { + std::map, int> connectionCount; + // Randomly select 2 sets of nodes and calculate the path between them. + // Keep track of how many times each connection between two neighbor nodes are used + for (int i = 0; i < 50; i++) + { + auto it1 = input.GetNodes().begin(); + auto it2 = input.GetNodes().begin(); + std::advance(it1, rand() % input.GetNodes().size()); + std::advance(it2, rand() % input.GetNodes().size()); + std::pair> paths = input.DijkstrasWithPath(*it1, *it2); + for (int j = 0; j < paths.second.size() - 1; j++) + { + std::pair connection; + if (paths.second[j] < paths.second[j + 1]) + connection = std::make_pair(paths.second[j], paths.second[j + 1]); + else + connection = std::make_pair(paths.second[j + 1], paths.second[j]); + connectionCount[connection]++; + } + } + + // Sort the connections based on number of uses, more uses likely means + // it's one of the 3 connections connecting the two group of nodes + std::vector> order; + for (auto& connection : connectionCount) + { + order.emplace_back(connection.first); + } + std::sort(order.begin(), + order.end(), + [&](const auto& lhs, const auto& rhs) { return connectionCount[lhs] > connectionCount[rhs]; }); + + // Go through the candidate connections starting with connections that were most visited. + for (int i = 0; i < order.size(); i++) + { + for (int j = i + 1; j < order.size(); j++) + { + for (int k = j + 1; k < order.size(); k++) + { + auto [filled, count] = FloodFill(input, {order[i], order[j], order[k]}); + if (filled) + { + return count * (input.GetNodes().size() - count); + } + } + } + } + + // Unlucky ¯\_(*_*)_/¯ + return 0; + } + + OUTPUT2(input) + { + std::cout << " ___ ___" << std::endl; + std::cout << " (o o) (o o)" << std::endl; + std::cout << "( V ) MERRY CHRISTMAS! ( V )" << std::endl; + std::cout << "--m-m----------------------m-m--" << std::endl; + return 0; + } +} diff --git a/src/common/aoc.h b/src/common/aoc.h index 4f770f6..65ec78e 100644 --- a/src/common/aoc.h +++ b/src/common/aoc.h @@ -2,6 +2,7 @@ #include #include +#include #include #include #include