diff --git a/fetch.sh b/fetch.sh index 710eaaf..dda8c13 100755 --- a/fetch.sh +++ b/fetch.sh @@ -8,13 +8,9 @@ fi list=$(find -name "Day*.cpp" | awk -F '/' '{print $3 "/" $4}' | awk -F '.' '{print $1}' | sed 's/Day//g' | sort) for item in $list; do - echo $item year=$(echo $item | awk -F '/' '{print $1}') day=$(echo $item | awk -F '/' '{print $2}') - echo "year: $year" - echo "day: $day" dir="res/$year/day$day/input.txt" - echo $dir if [ ! -e $dir ]; then webitem=$(echo $day | sed 's/^0//g') echo "Fetching $dir" diff --git a/res/2021/day01/test_input.txt b/res/2021/day01/test_input.txt new file mode 100644 index 0000000..59dad67 --- /dev/null +++ b/res/2021/day01/test_input.txt @@ -0,0 +1,10 @@ +199 +200 +208 +210 +200 +207 +240 +269 +260 +263 \ No newline at end of file diff --git a/res/2021/day02/test_input.txt b/res/2021/day02/test_input.txt new file mode 100644 index 0000000..0ed302d --- /dev/null +++ b/res/2021/day02/test_input.txt @@ -0,0 +1,6 @@ +forward 5 +down 5 +forward 8 +up 3 +down 8 +forward 2 \ No newline at end of file diff --git a/res/2021/day03/test_input.txt b/res/2021/day03/test_input.txt new file mode 100644 index 0000000..665fd57 --- /dev/null +++ b/res/2021/day03/test_input.txt @@ -0,0 +1,12 @@ +00100 +11110 +10110 +10111 +10101 +01111 +00111 +11100 +10000 +11001 +00010 +01010 \ No newline at end of file diff --git a/res/2021/day04/test_input.txt b/res/2021/day04/test_input.txt new file mode 100644 index 0000000..49d17bc --- /dev/null +++ b/res/2021/day04/test_input.txt @@ -0,0 +1,19 @@ +7,4,9,5,11,17,23,2,0,14,21,24,10,16,13,6,15,25,12,22,18,20,8,19,3,26,1 + +22 13 17 11 0 + 8 2 23 4 24 +21 9 14 16 7 + 6 10 3 18 5 + 1 12 20 15 19 + + 3 15 0 2 22 + 9 18 13 17 5 +19 8 7 25 23 +20 11 10 24 4 +14 21 16 12 6 + +14 21 17 24 4 +10 16 15 9 19 +18 8 23 26 20 +22 11 13 6 5 + 2 0 12 3 7 \ No newline at end of file diff --git a/res/2021/day05/test_input.txt b/res/2021/day05/test_input.txt new file mode 100644 index 0000000..1d4e36d --- /dev/null +++ b/res/2021/day05/test_input.txt @@ -0,0 +1,10 @@ +0,9 -> 5,9 +8,0 -> 0,8 +9,4 -> 3,4 +2,2 -> 2,1 +7,0 -> 7,4 +6,4 -> 2,0 +0,9 -> 2,9 +3,4 -> 1,4 +0,0 -> 8,8 +5,5 -> 8,2 \ No newline at end of file diff --git a/res/2021/day06/test_input.txt b/res/2021/day06/test_input.txt new file mode 100644 index 0000000..a7af2b1 --- /dev/null +++ b/res/2021/day06/test_input.txt @@ -0,0 +1 @@ +3,4,3,1,2 \ No newline at end of file diff --git a/res/2021/day07/test_input.txt b/res/2021/day07/test_input.txt new file mode 100644 index 0000000..2bdd92f --- /dev/null +++ b/res/2021/day07/test_input.txt @@ -0,0 +1 @@ +16,1,2,0,4,2,7,1,2,14 \ No newline at end of file diff --git a/res/2021/day08/test_input.txt b/res/2021/day08/test_input.txt new file mode 100644 index 0000000..8614893 --- /dev/null +++ b/res/2021/day08/test_input.txt @@ -0,0 +1,10 @@ +be cfbegad cbdgef fgaecd cgeb fdcge agebfd fecdb fabcd edb | fdgacbe cefdb cefbgd gcbe +edbfga begcd cbg gc gcadebf fbgde acbgfd abcde gfcbed gfec | fcgedb cgb dgebacf gc +fgaebd cg bdaec gdafb agbcfd gdcbef bgcad gfac gcb cdgabef | cg cg fdcagb cbg +fbegcd cbd adcefb dageb afcb bc aefdc ecdab fgdeca fcdbega | efabcd cedba gadfec cb +aecbfdg fbg gf bafeg dbefa fcge gcbea fcaegb dgceab fcbdga | gecf egdcabf bgf bfgea +fgeab ca afcebg bdacfeg cfaedg gcfdb baec bfadeg bafgc acf | gebdcfa ecba ca fadegcb +dbcfg fgd bdegcaf fgec aegbdf ecdfab fbedc dacgb gdcebf gf | cefg dcbef fcge gbcadfe +bdfegc cbegaf gecbf dfcage bdacg ed bedf ced adcbefg gebcd | ed bcgafe cdgba cbgef +egadfb cdbfeg cegd fecab cgb gbdefca cg fgcdab egfdb bfceg | gbdfcae bgc cg cgb +gcafb gcf dcaebfg ecagb gf abcdeg gaef cafbge fdbac fegbdc | fgae cfgab fg bagce \ No newline at end of file diff --git a/res/2021/day09/test_input.txt b/res/2021/day09/test_input.txt new file mode 100644 index 0000000..610bad9 --- /dev/null +++ b/res/2021/day09/test_input.txt @@ -0,0 +1,5 @@ +2199943210 +3987894921 +9856789892 +8767896789 +9899965678 \ No newline at end of file diff --git a/res/2021/day10/test_input.txt b/res/2021/day10/test_input.txt new file mode 100644 index 0000000..2f182d8 --- /dev/null +++ b/res/2021/day10/test_input.txt @@ -0,0 +1,10 @@ +[({(<(())[]>[[{[]{<()<>> +[(()[<>])]({[<{<<[]>>( +{([(<{}[<>[]}>{[]{[(<()> +(((({<>}<{<{<>}{[]{[]{} +[[<[([]))<([[{}[[()]]] +[{[{({}]{}}([{[{{{}}([] +{<[[]]>}<{[{[{[]{()[[[] +[<(<(<(<{}))><([]([]() +<{([([[(<>()){}]>(<<{{ +<{([{{}}[<[[[<>{}]]]>[]] \ No newline at end of file diff --git a/res/2021/day11/test_input.txt b/res/2021/day11/test_input.txt new file mode 100644 index 0000000..a3819c9 --- /dev/null +++ b/res/2021/day11/test_input.txt @@ -0,0 +1,10 @@ +5483143223 +2745854711 +5264556173 +6141336146 +6357385478 +4167524645 +2176841721 +6882881134 +4846848554 +5283751526 \ No newline at end of file diff --git a/res/2021/day12/test_input.txt b/res/2021/day12/test_input.txt new file mode 100644 index 0000000..6fd8c41 --- /dev/null +++ b/res/2021/day12/test_input.txt @@ -0,0 +1,7 @@ +start-A +start-b +A-c +A-b +b-d +A-end +b-end diff --git a/res/2021/day13/test_input.txt b/res/2021/day13/test_input.txt new file mode 100644 index 0000000..32a8563 --- /dev/null +++ b/res/2021/day13/test_input.txt @@ -0,0 +1,21 @@ +6,10 +0,14 +9,10 +0,3 +10,4 +4,11 +6,0 +6,12 +4,1 +0,13 +10,12 +3,4 +3,0 +8,4 +1,10 +2,14 +8,10 +9,0 + +fold along y=7 +fold along x=5 \ No newline at end of file diff --git a/res/2021/day14/test_input.txt b/res/2021/day14/test_input.txt new file mode 100644 index 0000000..6c1c3a1 --- /dev/null +++ b/res/2021/day14/test_input.txt @@ -0,0 +1,18 @@ +NNCB + +CH -> B +HH -> N +CB -> H +NH -> C +HB -> C +HC -> B +HN -> C +NN -> C +BH -> H +NC -> B +NB -> B +BN -> B +BB -> N +BC -> B +CC -> N +CN -> C \ No newline at end of file diff --git a/res/2021/day15/test_input.txt b/res/2021/day15/test_input.txt new file mode 100644 index 0000000..7d9d562 --- /dev/null +++ b/res/2021/day15/test_input.txt @@ -0,0 +1,10 @@ +1163751742 +1381373672 +2136511328 +3694931569 +7463417111 +1319128137 +1359912421 +3125421639 +1293138521 +2311944581 \ No newline at end of file diff --git a/res/2021/day16/test_input.txt b/res/2021/day16/test_input.txt new file mode 100644 index 0000000..4ced67a --- /dev/null +++ b/res/2021/day16/test_input.txt @@ -0,0 +1 @@ +9C0141080250320F1802104A08 diff --git a/res/2021/day17/test_input.txt b/res/2021/day17/test_input.txt new file mode 100644 index 0000000..f40609b --- /dev/null +++ b/res/2021/day17/test_input.txt @@ -0,0 +1 @@ +target area: x=20..30, y=-10..-5 \ No newline at end of file diff --git a/res/2021/day18/test_input.txt b/res/2021/day18/test_input.txt new file mode 100644 index 0000000..1368dc4 --- /dev/null +++ b/res/2021/day18/test_input.txt @@ -0,0 +1,10 @@ +[[[0,[5,8]],[[1,7],[9,6]]],[[4,[1,2]],[[1,4],2]]] +[[[5,[2,8]],4],[5,[[9,9],0]]] +[6,[[[6,2],[5,6]],[[7,6],[4,7]]]] +[[[6,[0,7]],[0,9]],[4,[9,[9,0]]]] +[[[7,[6,4]],[3,[1,3]]],[[[5,5],1],9]] +[[6,[[7,3],[3,2]]],[[[3,8],[5,7]],4]] +[[[[5,4],[7,7]],8],[[8,3],8]] +[[9,3],[[9,9],[6,[4,9]]]] +[[2,[[7,7],7]],[[5,8],[[9,3],[0,2]]]] +[[[[5,2],5],[8,[3,7]]],[[5,[7,5]],[4,4]]] diff --git a/res/2021/day19/test_input.txt b/res/2021/day19/test_input.txt new file mode 100644 index 0000000..b596cc4 --- /dev/null +++ b/res/2021/day19/test_input.txt @@ -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/res/2021/day20/test_input.txt b/res/2021/day20/test_input.txt new file mode 100644 index 0000000..000a554 --- /dev/null +++ b/res/2021/day20/test_input.txt @@ -0,0 +1,7 @@ +..#.#..#####.#.#.#.###.##.....###.##.#..###.####..#####..#....#..#..##..###..######.###...####..#..#####..##..#.#####...##.#.#..#.##..#.#......#.###.######.###.####...#.##.##..#..#..#####.....#.#....###..#.##......#.....#..#..#..##..#...##.######.####.####.#.#...#.......#..#.#.#...####.##.#......#..#...##.#.##..#...##.#.##..###.#......#.#.......#.#.#.####.###.##...#.....####.#..#..#.##.#....##..#.####....##...##..#...#......#.#.......#.......##..####..#...#.#.#...##..#.#..###..#####........#..####......#..# + +#..#. +#.... +##..# +..#.. +..### \ No newline at end of file diff --git a/res/2021/day21/test_input.txt b/res/2021/day21/test_input.txt new file mode 100644 index 0000000..6dec3f4 --- /dev/null +++ b/res/2021/day21/test_input.txt @@ -0,0 +1,2 @@ +Player 1 starting position: 4 +Player 2 starting position: 8 \ No newline at end of file diff --git a/res/2021/day22/test_input.txt b/res/2021/day22/test_input.txt new file mode 100644 index 0000000..2790bed --- /dev/null +++ b/res/2021/day22/test_input.txt @@ -0,0 +1,60 @@ +on x=-5..47,y=-31..22,z=-19..33 +on x=-44..5,y=-27..21,z=-14..35 +on x=-49..-1,y=-11..42,z=-10..38 +on x=-20..34,y=-40..6,z=-44..1 +off x=26..39,y=40..50,z=-2..11 +on x=-41..5,y=-41..6,z=-36..8 +off x=-43..-33,y=-45..-28,z=7..25 +on x=-33..15,y=-32..19,z=-34..11 +off x=35..47,y=-46..-34,z=-11..5 +on x=-14..36,y=-6..44,z=-16..29 +on x=-57795..-6158,y=29564..72030,z=20435..90618 +on x=36731..105352,y=-21140..28532,z=16094..90401 +on x=30999..107136,y=-53464..15513,z=8553..71215 +on x=13528..83982,y=-99403..-27377,z=-24141..23996 +on x=-72682..-12347,y=18159..111354,z=7391..80950 +on x=-1060..80757,y=-65301..-20884,z=-103788..-16709 +on x=-83015..-9461,y=-72160..-8347,z=-81239..-26856 +on x=-52752..22273,y=-49450..9096,z=54442..119054 +on x=-29982..40483,y=-108474..-28371,z=-24328..38471 +on x=-4958..62750,y=40422..118853,z=-7672..65583 +on x=55694..108686,y=-43367..46958,z=-26781..48729 +on x=-98497..-18186,y=-63569..3412,z=1232..88485 +on x=-726..56291,y=-62629..13224,z=18033..85226 +on x=-110886..-34664,y=-81338..-8658,z=8914..63723 +on x=-55829..24974,y=-16897..54165,z=-121762..-28058 +on x=-65152..-11147,y=22489..91432,z=-58782..1780 +on x=-120100..-32970,y=-46592..27473,z=-11695..61039 +on x=-18631..37533,y=-124565..-50804,z=-35667..28308 +on x=-57817..18248,y=49321..117703,z=5745..55881 +on x=14781..98692,y=-1341..70827,z=15753..70151 +on x=-34419..55919,y=-19626..40991,z=39015..114138 +on x=-60785..11593,y=-56135..2999,z=-95368..-26915 +on x=-32178..58085,y=17647..101866,z=-91405..-8878 +on x=-53655..12091,y=50097..105568,z=-75335..-4862 +on x=-111166..-40997,y=-71714..2688,z=5609..50954 +on x=-16602..70118,y=-98693..-44401,z=5197..76897 +on x=16383..101554,y=4615..83635,z=-44907..18747 +off x=-95822..-15171,y=-19987..48940,z=10804..104439 +on x=-89813..-14614,y=16069..88491,z=-3297..45228 +on x=41075..99376,y=-20427..49978,z=-52012..13762 +on x=-21330..50085,y=-17944..62733,z=-112280..-30197 +on x=-16478..35915,y=36008..118594,z=-7885..47086 +off x=-98156..-27851,y=-49952..43171,z=-99005..-8456 +off x=2032..69770,y=-71013..4824,z=7471..94418 +on x=43670..120875,y=-42068..12382,z=-24787..38892 +off x=37514..111226,y=-45862..25743,z=-16714..54663 +off x=25699..97951,y=-30668..59918,z=-15349..69697 +off x=-44271..17935,y=-9516..60759,z=49131..112598 +on x=-61695..-5813,y=40978..94975,z=8655..80240 +off x=-101086..-9439,y=-7088..67543,z=33935..83858 +off x=18020..114017,y=-48931..32606,z=21474..89843 +off x=-77139..10506,y=-89994..-18797,z=-80..59318 +off x=8476..79288,y=-75520..11602,z=-96624..-24783 +on x=-47488..-1262,y=24338..100707,z=16292..72967 +off x=-84341..13987,y=2429..92914,z=-90671..-1318 +off x=-37810..49457,y=-71013..-7894,z=-105357..-13188 +off x=-27365..46395,y=31009..98017,z=15428..76570 +off x=-70369..-16548,y=22648..78696,z=-1892..86821 +on x=-53470..21291,y=-120233..-33476,z=-44150..38147 +off x=-93533..-4276,y=-16170..68771,z=-104985..-24507 \ No newline at end of file diff --git a/res/2021/day23/test_input.txt b/res/2021/day23/test_input.txt new file mode 100644 index 0000000..56a84a1 --- /dev/null +++ b/res/2021/day23/test_input.txt @@ -0,0 +1,5 @@ +############# +#...........# +###B#C#B#D### + #A#D#C#A# + ######### \ No newline at end of file diff --git a/res/2021/day24/test_input.txt b/res/2021/day24/test_input.txt new file mode 100644 index 0000000..e69de29 diff --git a/res/2021/day25/test_input.txt b/res/2021/day25/test_input.txt new file mode 100644 index 0000000..0f3c0cd --- /dev/null +++ b/res/2021/day25/test_input.txt @@ -0,0 +1,9 @@ +v...>>.vv> +.vv>>.vv.. +>>.>v>...v +>>v>>.>.v. +v>v.vv.v.. +>.>>..v... +.vv..>.>v. +v.v..>>v.v +....v..v.> diff --git a/src/2021/Day01.cpp b/src/2021/Day01.cpp new file mode 100644 index 0000000..711dae4 --- /dev/null +++ b/src/2021/Day01.cpp @@ -0,0 +1,38 @@ +#include "common/aoc.h" + +namespace y2021::day01 +{ + REGISTER_DAY(2021, Day01, std::vector, int); + + REGISTER_TEST_EXAMPLE(2021, Day01, ExampleInput, 1, 7); + REGISTER_TEST(2021, Day01, Input, 1, 1722); + REGISTER_TEST_EXAMPLE(2021, Day01, ExampleInput, 2, 5); + REGISTER_TEST(2021, Day01, Input, 2, 1748); + + READ_INPUT(input) + { + return Input::ReadInts(input); + } + + OUTPUT1(input) + { + int count = 0; + for (size_t i = 0; i < input.size() - 1; i++) + { + if (input[i] < input[i + 1]) + count++; + } + return count; + } + + OUTPUT2(input) + { + int count = 0; + for (size_t i = 0; i < input.size() - 3; i++) + { + if (input[i] + input[i + 1] + input[i + 2] < input[i + 1] + input[i + 2] + input[i + 3]) + count++; + } + return count; + } +} diff --git a/src/2021/Day02.cpp b/src/2021/Day02.cpp new file mode 100644 index 0000000..d2842e9 --- /dev/null +++ b/src/2021/Day02.cpp @@ -0,0 +1,90 @@ +#include "common/aoc.h" + +namespace y2021::day02 +{ + enum class Dir + { + Forward, + Down, + Up + }; + + struct Move + { + Dir dir; + int amount; + }; + + REGISTER_DAY(2021, Day02, std::vector, int); + + REGISTER_TEST_EXAMPLE(2021, Day02, ExampleInput, 1, 150); + REGISTER_TEST(2021, Day02, Input, 1, 1815044); + REGISTER_TEST_EXAMPLE(2021, Day02, ExampleInput, 2, 900); + REGISTER_TEST(2021, Day02, Input, 2, 1739283308); + + READ_INPUT(input) + { + std::vector moves; + std::string str; + while (getline(input, str)) + { + std::stringstream ss{str}; + std::string s; + int i; + ss >> s >> i; + if (s == "forward") + moves.emplace_back(Move{Dir::Forward, i}); + else if (s == "down") + moves.emplace_back(Move{Dir::Down, i}); + else if (s == "up") + moves.emplace_back(Move{Dir::Up, i}); + } + return moves; + } + + OUTPUT1(input) + { + int x = 0; + int y = 0; + for (auto&& move : input) + { + switch (move.dir) + { + case Dir::Forward: + x += move.amount; + break; + case Dir::Down: + y += move.amount; + break; + case Dir::Up: + y -= move.amount; + break; + } + } + return x * y; + } + + OUTPUT2(input) + { + int x = 0; + int y = 0; + int aim = 0; + for (auto&& move : input) + { + switch (move.dir) + { + case Dir::Forward: + x += move.amount; + y += aim * move.amount; + break; + case Dir::Down: + aim += move.amount; + break; + case Dir::Up: + aim -= move.amount; + break; + } + } + return x * y; + } +} diff --git a/src/2021/Day03.cpp b/src/2021/Day03.cpp new file mode 100644 index 0000000..f025a5b --- /dev/null +++ b/src/2021/Day03.cpp @@ -0,0 +1,94 @@ +#include "common/aoc.h" + +namespace y2021::day03 +{ + REGISTER_DAY(2021, Day03, std::set, int); + + REGISTER_TEST_EXAMPLE(2021, Day03, ExampleInput, 1, 198); + REGISTER_TEST(2021, Day03, Input, 1, 2498354); + REGISTER_TEST_EXAMPLE(2021, Day03, ExampleInput, 2, 230); + REGISTER_TEST(2021, Day03, Input, 2, 3277956); + + READ_INPUT(input) + { + std::set strings; + std::string str; + while (getline(input, str)) + { + strings.emplace(str); + } + return strings; + } + + OUTPUT1(input) + { + int gamma = 0; + int width = input.begin()->size(); + for (int x = 0; x < width; x++) + { + size_t count = 0; + for (auto&& str : input) + { + if (str[x] == '1') + count++; + } + if (count >= input.size() / 2) + { + gamma |= BIT(width - x - 1); + } + } + int invertMask = BIT(width) - 1; + return gamma * (gamma ^ invertMask); + } + + OUTPUT2(input) + { + int width = input.begin()->size(); + + std::set oxygen = input; + std::set carbon = input; + for (int i = 0; i < width; i++) + { + char mostCommon = '0'; + size_t count = 0; + for (auto it = oxygen.begin(); it != oxygen.end(); it++) + { + if (it->at(i) == '1') + count++; + } + if (count * 2 >= oxygen.size()) + mostCommon = '1'; + for (auto it = oxygen.begin(); it != oxygen.end();) + { + if (oxygen.size() == 1) + break; + if (it->at(i) != mostCommon) + it = oxygen.erase(it); + else + it++; + } + + char leastCommon = '0'; + count = 0; + for (auto it = carbon.begin(); it != carbon.end(); it++) + { + if (it->at(i) == '1') + count++; + } + if (count * 2 < carbon.size()) + leastCommon = '1'; + for (auto it = carbon.begin(); it != carbon.end();) + { + if (carbon.size() == 1) + break; + if (it->at(i) != leastCommon) + it = carbon.erase(it); + else + it++; + } + } + int oxygenVal = Helper::BinStrToInt(*oxygen.begin()); + int carbonVal = Helper::BinStrToInt(*carbon.begin()); + return oxygenVal * carbonVal; + } +} diff --git a/src/2021/Day04.cpp b/src/2021/Day04.cpp new file mode 100644 index 0000000..2d7818b --- /dev/null +++ b/src/2021/Day04.cpp @@ -0,0 +1,127 @@ +#include "common/aoc.h" + +namespace y2021::day04 +{ + struct Game + { + std::vector input; + std::vector> boards; + }; + + bool MarkAndCheck(Array2D& board, int number) + { + Index2D index = board.Find(number); + if (index) + { + board[index] = -1; + // Check board is filled + bool filledX = true; + bool filledY = true; + for (int i = 0; i < 5; i++) + { + if (board.Get(i, index.y) != -1) + filledX = false; + if (board.Get(index.x, i) != -1) + filledY = false; + } + return filledX || filledY; + } + return false; + } + + REGISTER_DAY(2021, Day04, Game, int); + + REGISTER_TEST_EXAMPLE(2021, Day04, ExampleInput, 1, 4512); + REGISTER_TEST(2021, Day04, Input, 1, 54275); + REGISTER_TEST_EXAMPLE(2021, Day04, ExampleInput, 2, 1924); + REGISTER_TEST(2021, Day04, Input, 2, 13158); + + READ_INPUT(input) + { + Game game; + std::string str; + getline(input, str); + std::stringstream ss{str}; + while (getline(ss, str, ',')) + { + game.input.emplace_back(atoi(str.c_str())); + } + getline(input, str); + while (!input.eof()) + { + Array2D array2D{5, 5}; + for (int y = 0; y < 5; y++) + { + getline(input, str); + std::stringstream ss{str}; + for (int x = 0; x < 5; x++) + { + int i; + ss >> i; + array2D.Set(x, y, i); + } + } + game.boards.emplace_back(array2D); + getline(input, str); + } + return game; + } + + OUTPUT1(input) + { + Game game = input; // Copy the data to be able to modify it + int winner = -1; + int i = 0; + while (winner == -1) + { + int number = game.input[i]; + for (auto& board : game.boards) + { + if (MarkAndCheck(board, number)) + { + int total = 0; + for (auto& val : board) + { + if (val != -1) + total += val; + } + return total * number; + } + } + i++; + } + return 0; + } + + OUTPUT2(input) + { + Game game = input; // Copy the data to be able to modify it + int winner = -1; + int i = 0; + while (winner == -1) + { + int number = game.input[i]; + for (auto it = game.boards.begin(); it != game.boards.end();) + { + if (MarkAndCheck(*it, number)) + { + if (game.boards.size() == 1) + { + int total = 0; + for (auto& val : game.boards.front()) + { + if (val != -1) + total += val; + } + return total * number; + } + it = game.boards.erase(it); + } + else + it++; + } + i++; + } + return 0; + } +} diff --git a/src/2021/Day05.cpp b/src/2021/Day05.cpp new file mode 100644 index 0000000..625dbbb --- /dev/null +++ b/src/2021/Day05.cpp @@ -0,0 +1,96 @@ +#include "common/aoc.h" + +namespace y2021::day05 +{ + struct Line + { + int x1; + int y1; + int x2; + int y2; + }; + + REGISTER_DAY(2021, Day05, std::vector, int); + + REGISTER_TEST_EXAMPLE(2021, Day05, ExampleInput, 1, 5); + REGISTER_TEST(2021, Day05, Input, 1, 5442); + REGISTER_TEST_EXAMPLE(2021, Day05, ExampleInput, 2, 12); + REGISTER_TEST(2021, Day05, Input, 2, 19571); + + READ_INPUT(input) + { + std::string str; + std::vector lines; + while (getline(input, str)) + { + std::stringstream ss{str}; + Line line; + ss >> line.x1 >> "," >> line.y1 >> "->" >> line.x2 >> "," >> line.y2; + lines.emplace_back(line); + } + return lines; + } + + OUTPUT1(input) + { + Index2D max; + for (auto& line : input) + { + max.x = std::max({max.x, line.x1, line.x2}); + max.y = std::max({max.y, line.y1, line.y2}); + } + Array2D overlap{max.x + 1, max.y + 1}; + for (auto& line : input) + { + if (line.x1 == line.x2 || line.y1 == line.y2) + { + int width = std::max(std::abs(line.x1 - line.x2), std::abs(line.y1 - line.y2)) + 1; + int deltaX = Helper::Sign(line.x2 - line.x1); + int deltaY = Helper::Sign(line.y2 - line.y1); + for (int i = 0; i < width; i++) + { + Index2D index{line.x1 + deltaX * i, line.y1 + deltaY * i}; + overlap[index]++; + } + } + } + int count = 0; + overlap.Each( + [&](const Array2D& overlap, const Index2D& index) + { + if (overlap[index] > 1) + count++; + }); + return count; + } + + OUTPUT2(input) + { + Index2D max; + for (auto& line : input) + { + max.x = std::max({max.x, line.x1, line.x2}); + max.y = std::max({max.y, line.y1, line.y2}); + } + Array2D overlap{max.x + 1, max.y + 1}; + for (auto& line : input) + { + int width = std::max(std::abs(line.x1 - line.x2), std::abs(line.y1 - line.y2)) + 1; + int deltaX = Helper::Sign(line.x2 - line.x1); + int deltaY = Helper::Sign(line.y2 - line.y1); + for (int i = 0; i < width; i++) + { + Index2D index{line.x1 + deltaX * i, line.y1 + deltaY * i}; + overlap[index]++; + } + } + int count = 0; + overlap.Each( + [&](const Array2D& overlap, const Index2D& index) + { + if (overlap[index] > 1) + count++; + }); + return count; + } +} diff --git a/src/2021/Day06.cpp b/src/2021/Day06.cpp new file mode 100644 index 0000000..26b243c --- /dev/null +++ b/src/2021/Day06.cpp @@ -0,0 +1,52 @@ +#include "common/aoc.h" + +namespace y2021::day06 +{ + + int64_t GetPopulation(std::vector& lenternfishes, int days) + { + for (int i = 0; i < days; i++) + { + int64_t newBorn = lenternfishes[0]; + for (size_t j = 1; j < lenternfishes.size(); j++) + { + lenternfishes[j - 1] = lenternfishes[j]; + } + lenternfishes[8] = newBorn; + lenternfishes[6] += newBorn; + } + return Helper::Sum(lenternfishes); + } + + REGISTER_DAY(2021, Day06, std::vector, int64_t); + + REGISTER_TEST_EXAMPLE(2021, Day06, ExampleInput, 1, 5934); + REGISTER_TEST(2021, Day06, Input, 1, 374994); + REGISTER_TEST_EXAMPLE(2021, Day06, ExampleInput, 2, 26984457539); + REGISTER_TEST(2021, Day06, Input, 2, 1686252324092); + + READ_INPUT(input) + { + std::vector ints; + ints.resize(9); + + std::string str; + while (getline(input, str, ',')) + { + ints[atoi(str.c_str())]++; + } + return ints; + } + + OUTPUT1(inputConst) + { + InputType input = inputConst; + return GetPopulation(input, 80); + } + + OUTPUT2(inputConst) + { + InputType input = inputConst; + return GetPopulation(input, 256); + } +} diff --git a/src/2021/Day07.cpp b/src/2021/Day07.cpp new file mode 100644 index 0000000..c8b0207 --- /dev/null +++ b/src/2021/Day07.cpp @@ -0,0 +1,51 @@ +#include "common/aoc.h" + +namespace y2021::day07 +{ + REGISTER_DAY(2021, Day07, std::vector, int); + + REGISTER_TEST_EXAMPLE(2021, Day07, ExampleInput, 1, 37); + REGISTER_TEST(2021, Day07, Input, 1, 359648); + REGISTER_TEST_EXAMPLE(2021, Day07, ExampleInput, 2, 168); + REGISTER_TEST(2021, Day07, Input, 2, 100727924); + + READ_INPUT(input) + { + return Input::ReadInts(input, ','); + } + + OUTPUT1(input) + { + int max = Helper::Max(input); + int min = Helper::Min(input); + int minCost = std::numeric_limits::max(); + for (int i = min; i < max + 1; i++) + { + int cost = 0; + for (auto num : input) + { + cost += std::abs(num - i); + } + minCost = std::min(minCost, cost); + } + return minCost; + } + + OUTPUT2(input) + { + int max = Helper::Max(input); + int min = Helper::Min(input); + int minCost = std::numeric_limits::max(); + for (int i = min; i < max + 1; i++) + { + int cost = 0; + for (auto num : input) + { + int steps = std::abs(num - i); + cost += steps * (steps + 1) / 2; + } + minCost = std::min(minCost, cost); + } + return minCost; + } +} diff --git a/src/2021/Day08.cpp b/src/2021/Day08.cpp new file mode 100644 index 0000000..3483ddc --- /dev/null +++ b/src/2021/Day08.cpp @@ -0,0 +1,154 @@ +#include "common/aoc.h" + +namespace y2021::day08 +{ + struct Signal + { + std::vector> numbers; + std::vector> display; + }; + + REGISTER_DAY(2021, Day08, std::vector, int); + + REGISTER_TEST_EXAMPLE(2021, Day08, ExampleInput, 1, 26); + REGISTER_TEST(2021, Day08, Input, 1, 301); + REGISTER_TEST_EXAMPLE(2021, Day08, ExampleInput, 2, 61229); + REGISTER_TEST(2021, Day08, Input, 2, 908067); + + READ_INPUT(input) + { + std::string str; + std::vector signals; + while (getline(input, str)) + { + Signal signal; + std::stringstream ss{str}; + for (int i = 0; i < 10; i++) + { + std::string s; + ss >> s; + std::set set; + for (char c : s) set.emplace(c); + signal.numbers.emplace_back(set); + } + char c; + ss >> c; + for (int i = 0; i < 4; i++) + { + std::string s; + ss >> s; + std::set set; + for (char c : s) set.emplace(c); + signal.display.emplace_back(set); + } + signals.emplace_back(signal); + } + return signals; + } + + OUTPUT1(input) + { + int count = 0; + for (auto&& signal : input) + { + for (auto&& number : signal.display) + { + if (number.size() == 2 || number.size() == 3 || number.size() == 4 || number.size() == 7) + count++; + } + } + return count; + } + + OUTPUT2(input) + { + int sum = 0; + for (auto&& signal : input) + { + std::vector> numbers(10); + // Find all numbers with unique amount of segments + for (auto&& number : signal.numbers) + { + if (number.size() == 2) + numbers[1] = number; + if (number.size() == 3) + numbers[7] = number; + if (number.size() == 4) + numbers[4] = number; + if (number.size() == 7) + numbers[8] = number; + } + + // Find all numbers with five and six segments + std::vector> fivesegs; + std::vector> sixsegs; + for (auto&& number : signal.numbers) + { + if (number.size() == 5) + fivesegs.emplace_back(number); + if (number.size() == 6) + sixsegs.emplace_back(number); + } + + // Find number 3 (only five segment number which 1 is a subset of) + for (auto it = fivesegs.begin(); it != fivesegs.end(); it++) + { + if (Helper::IsSubset(numbers[1], *it)) + { + numbers[3] = *it; + fivesegs.erase(it); + break; + } + } + + // Find number 6 (only six segment number which is not a subset of 1) + for (auto it = sixsegs.begin(); it != sixsegs.end(); it++) + { + if (!Helper::IsSubset(numbers[1], *it)) + { + numbers[6] = *it; + sixsegs.erase(it); + break; + } + } + + // Find number 5 (only five segment number which is a subset of 6) + for (auto it = fivesegs.begin(); it != fivesegs.end(); it++) + { + if (Helper::IsSubset(*it, numbers[6])) + { + numbers[5] = *it; + fivesegs.erase(it); + break; + } + } + // Find number 2 (last five segment number) + numbers[2] = fivesegs.front(); + + // Find number 9 (only six segment number which 4 is a subset of) + for (auto it = sixsegs.begin(); it != sixsegs.end(); it++) + { + if (Helper::IsSubset(numbers[4], *it)) + { + numbers[9] = *it; + sixsegs.erase(it); + break; + } + } + + // Find number 0 (last six segment number) + numbers[0] = sixsegs.front(); + + // Calculate the segment display + int num = 0; + for (auto number : signal.display) + { + auto it = std::find(numbers.begin(), numbers.end(), number); + int i = it - numbers.begin(); + num = num * 10 + i; + } + sum += num; + } + return sum; + } +} diff --git a/src/2021/Day09.cpp b/src/2021/Day09.cpp new file mode 100644 index 0000000..2ebfe44 --- /dev/null +++ b/src/2021/Day09.cpp @@ -0,0 +1,91 @@ +#include "common/aoc.h" + +namespace y2021::day09 +{ + REGISTER_DAY(2021, Day09, Array2D, int); + + REGISTER_TEST_EXAMPLE(2021, Day09, ExampleInput, 1, 15); + REGISTER_TEST(2021, Day09, Input, 1, 478); + REGISTER_TEST_EXAMPLE(2021, Day09, ExampleInput, 2, 1134); + REGISTER_TEST(2021, Day09, Input, 2, 1327014); + + READ_INPUT(input) + { + return Input::ReadDigitsAsArray2D(input); + } + + OUTPUT1(input) + { + int count = 0; + Index2D index{0, 0}; + for (auto& val : input) + { + Index2D indices[4]{ + {index.x - 1, index.y}, {index.x, index.y - 1}, {index.x + 1, index.y}, {index.x, index.y + 1}}; + bool all = true; + for (auto& ind : indices) + { + if (input.IsInside(ind) && input[ind] <= val) + { + all = false; + break; + } + } + if (all) + count += val + 1; + input.Increment(index); + } + return count; + } + + OUTPUT2(input) + { + std::vector basins; + Index2D index{0, 0}; + for (auto& val : input) + { + Index2D indices[4]{ + {index.x - 1, index.y}, {index.x, index.y - 1}, {index.x + 1, index.y}, {index.x, index.y + 1}}; + bool all = true; + for (auto& ind : indices) + { + if (input.IsInside(ind) && input[ind] <= val) + { + all = false; + break; + } + } + if (all) + basins.emplace_back(index); + input.Increment(index); + } + + std::vector numbers; + for (auto&& basin : basins) + { + std::stack stack; + std::set visited; + stack.emplace(basin); + visited.emplace(basin); + while (!stack.empty()) + { + auto index = stack.top(); + stack.pop(); + Index2D indices[4]{ + {index.x - 1, index.y}, {index.x, index.y - 1}, {index.x + 1, index.y}, {index.x, index.y + 1}}; + for (auto&& ind : indices) + { + if (input.IsInside(ind) && input[ind] != 9 && visited.find(ind) == visited.end()) + { + stack.emplace(ind); + visited.emplace(ind); + } + } + } + numbers.emplace_back(visited.size()); + } + + std::sort(numbers.begin(), numbers.end()); + return numbers[numbers.size() - 1] * numbers[numbers.size() - 2] * numbers[numbers.size() - 3]; + } +} diff --git a/src/2021/Day10.cpp b/src/2021/Day10.cpp new file mode 100644 index 0000000..a4ab0d1 --- /dev/null +++ b/src/2021/Day10.cpp @@ -0,0 +1,90 @@ +#include "common/aoc.h" + +namespace y2021::day10 +{ + REGISTER_DAY(2021, Day10, std::vector, int64_t); + + REGISTER_TEST_EXAMPLE(2021, Day10, ExampleInput, 1, 26397); + REGISTER_TEST(2021, Day10, Input, 1, 240123); + REGISTER_TEST_EXAMPLE(2021, Day10, ExampleInput, 2, 288957); + REGISTER_TEST(2021, Day10, Input, 2, 3260812321); + + READ_INPUT(input) + { + return Input::ReadLines(input); + } + + OUTPUT1(input) + { + std::map values{{')', 3}, {']', 57}, {'}', 1197}, {'>', 25137}}; + Mapper mapper{{'(', ')'}, {'[', ']'}, {'{', '}'}, {'<', '>'}}; + int64_t count = 0; + for (auto&& str : input) + { + std::stack stack; + stack.emplace(str[0]); + for (size_t i = 1; i < str.size(); i++) + { + char* ptr = mapper.GetFirst(str[i]); + if (ptr) + { + if (stack.top() != *ptr) + { + count += values[str[i]]; + break; + } + stack.pop(); + } + else + { + stack.emplace(str[i]); + } + } + } + return count; + } + + OUTPUT2(input) + { + Mapper mapper{{'(', ')'}, {'[', ']'}, {'{', '}'}, {'<', '>'}}; + std::map values{{'(', 1}, {'[', 2}, {'{', 3}, {'<', 4}}; + int64_t count = 0; + std::vector scores; + for (auto&& str : input) + { + std::stack stack; + stack.emplace(str[0]); + bool valid = true; + for (size_t i = 1; i < str.size(); i++) + { + char* ptr = mapper.GetFirst(str[i]); + if (ptr) + { + if (stack.top() != *ptr) + { + valid = false; + break; + } + stack.pop(); + } + else + { + stack.emplace(str[i]); + } + } + if (valid) + { + int64_t score = 0; + while (!stack.empty()) + { + score *= 5; + score += values[stack.top()]; + stack.pop(); + } + scores.emplace_back(score); + } + } + std::sort(scores.begin(), scores.end()); + return scores[scores.size() / 2]; + } +} diff --git a/src/2021/Day11.cpp b/src/2021/Day11.cpp new file mode 100644 index 0000000..c5fb8df --- /dev/null +++ b/src/2021/Day11.cpp @@ -0,0 +1,82 @@ +#include "common/aoc.h" + +namespace y2021::day11 +{ + + std::set CalculateFlashing(Array2D& array) + { + for (auto& val : array) val++; + std::set flashed; + bool hasChanged = true; + while (hasChanged) + { + Array2D arrayCpy = array; + hasChanged = false; + Index2D index{0, 0}; + for (auto& val : array) + { + if (val > 9 && flashed.find(index) == flashed.end()) + { + flashed.emplace(index); + hasChanged = true; + for (int y = -1; y < 2; y++) + { + for (int x = -1; x < 2; x++) + { + if (x == 0 && y == 0) + continue; + Index2D newIndex{index.x + x, index.y + y}; + if (array.IsInside(newIndex)) + arrayCpy[newIndex]++; + } + } + } + array.Increment(index); + } + array = arrayCpy; + } + return flashed; + } + + REGISTER_DAY(2021, Day11, Array2D, int); + + REGISTER_TEST_EXAMPLE(2021, Day11, ExampleInput, 1, 1656); + REGISTER_TEST(2021, Day11, Input, 1, 1667); + REGISTER_TEST_EXAMPLE(2021, Day11, ExampleInput, 2, 195); + REGISTER_TEST(2021, Day11, Input, 2, 488); + + READ_INPUT(input) + { + return Input::ReadDigitsAsArray2D(input); + } + + OUTPUT1(input) + { + InputType array = input; + + int count = 0; + for (int i = 0; i < 100; i++) + { + std::set flashed = CalculateFlashing(array); + count += flashed.size(); + for (auto& flash : flashed) array[flash] = 0; + } + return count; + } + + OUTPUT2(input) + { + InputType array = input; + + int count = 1; + while (true) + { + std::set flashed = CalculateFlashing(array); + if (flashed.size() == array.width * array.height) + return count; + for (auto& flash : flashed) array[flash] = 0; + count++; + } + return count; + } +} diff --git a/src/2021/Day12.cpp b/src/2021/Day12.cpp new file mode 100644 index 0000000..c739708 --- /dev/null +++ b/src/2021/Day12.cpp @@ -0,0 +1,128 @@ +#include "common/aoc.h" + +namespace y2021::day12 +{ + struct Node + { + std::set connections; + }; + + struct Path + { + std::vector path{"start"}; + bool hasDuplicate = false; + }; + + using NodeMap = std::map; + REGISTER_DAY(2021, Day12, NodeMap, int); + + REGISTER_TEST_EXAMPLE(2021, Day12, ExampleInput, 1, 10); + REGISTER_TEST(2021, Day12, Input, 1, 3887); + REGISTER_TEST_EXAMPLE(2021, Day12, ExampleInput, 2, 36); + REGISTER_TEST(2021, Day12, Input, 2, 104834); + + READ_INPUT(input) + { + NodeMap nodes; + std::string str; + while (getline(input, str)) + { + std::stringstream ss{str}; + std::string from, to; + getline(ss, from, '-'); + getline(ss, to); + auto itFrom = nodes.find(from); + auto itTo = nodes.find(to); + if (itFrom == nodes.end()) + itFrom = nodes.emplace(from, Node{}).first; + if (itTo == nodes.end()) + itTo = nodes.emplace(to, Node{}).first; + + itFrom->second.connections.emplace(to); + itTo->second.connections.emplace(from); + } + return nodes; + } + + OUTPUT1(input) + { + int count = 0; + std::stack> paths; + paths.emplace(std::vector{"start"}); + while (!paths.empty()) + { + auto path = paths.top(); + paths.pop(); + if (path.back() == "end") + { + count++; + continue; + } + auto it = input.find(path.back()); + for (auto& connection : it->second.connections) + { + if (connection[0] >= 'a' && connection[0] <= 'z') + { + if (std::find(path.begin(), path.end(), connection) == path.end()) + { + std::vector cpy = path; + cpy.emplace_back(connection); + paths.emplace(cpy); + } + } + else + { + std::vector cpy = path; + cpy.emplace_back(connection); + paths.emplace(cpy); + } + } + } + return count; + } + + OUTPUT2(input) + { + int count = 0; + std::stack paths; + paths.emplace(Path{}); + while (!paths.empty()) + { + auto path = paths.top(); + paths.pop(); + if (path.path.back() == "end") + { + count++; + continue; + } + + auto it = input.find(path.path.back()); + for (auto& connection : it->second.connections) + { + if (connection[0] >= 'a' && connection[0] <= 'z') + { + if (std::find(path.path.begin(), path.path.end(), connection) == path.path.end()) + { + Path cpy = path; + cpy.path.emplace_back(connection); + paths.emplace(cpy); + } + else if (!path.hasDuplicate && connection != "start") + { + Path cpy = path; + cpy.path.emplace_back(connection); + cpy.hasDuplicate = true; + paths.emplace(cpy); + } + } + else + { + Path cpy = path; + cpy.path.emplace_back(connection); + paths.emplace(cpy); + } + } + } + return count; + } +} diff --git a/src/2021/Day13.cpp b/src/2021/Day13.cpp new file mode 100644 index 0000000..885e6e8 --- /dev/null +++ b/src/2021/Day13.cpp @@ -0,0 +1,100 @@ +#include "common/aoc.h" + +namespace y2021::day13 +{ + + void Fold(std::set& indices, bool x, int fold) + { + std::set newIndices; + for (auto& index : indices) + { + // y axis + if (x) + { + Index2D newIndex = index; + if (newIndex.x > fold) + newIndex.x = fold - (newIndex.x - 1) % fold - 1; + newIndices.emplace(newIndex); + } + else + { + Index2D newIndex = index; + if (newIndex.y > fold) + newIndex.y = fold - (newIndex.y - 1) % fold - 1; + newIndices.emplace(newIndex); + } + } + indices = newIndices; + } + + struct Paper + { + std::set indices; + std::vector> folds; + }; + + REGISTER_DAY(2021, Day13, Paper, int); + + REGISTER_TEST_EXAMPLE(2021, Day13, ExampleInput, 1, 17); + REGISTER_TEST(2021, Day13, Input, 1, 716); + REGISTER_TEST_EXAMPLE(2021, Day13, ExampleInput, 2, 16); + REGISTER_TEST(2021, Day13, Input, 2, 97); + + READ_INPUT(input) + { + Paper paper; + std::string str; + while (getline(input, str)) + { + if (str.empty()) + break; + Index2D index; + std::stringstream ss{str}; + ss >> index.x >> "," >> index.y; + paper.indices.emplace(index); + } + + while (getline(input, str)) + { + char c, c2; + std::string s; + int i; + std::stringstream ss{str}; + ss >> s >> s >> c >> c2 >> i; + paper.folds.emplace_back(c == 'x', i); + } + return paper; + } + + OUTPUT1(input) + { + std::set indices = input.indices; + Fold(indices, input.folds.front().first, input.folds.front().second); + return indices.size(); + } + + OUTPUT2(input) + { + std::set indices = input.indices; + for (auto& fold : input.folds) + { + Fold(indices, fold.first, fold.second); + } + + Index2D max = *indices.begin(); + for (auto index : indices) + { + max.x = std::max(index.x, max.x); + max.y = std::max(index.y, max.y); + } + Array2D board{max.x + 1, max.y + 1}; + board.Fill(' '); + for (auto& index : indices) + { + board[index] = '#'; + } + std::cout << board; + // Not the "correct" output to part 2, but it verifies something at least + return indices.size(); + } +} diff --git a/src/2021/Day14.cpp b/src/2021/Day14.cpp new file mode 100644 index 0000000..8e8cd90 --- /dev/null +++ b/src/2021/Day14.cpp @@ -0,0 +1,121 @@ +#include "common/aoc.h" + +namespace y2021::day14 +{ + struct Data + { + std::string str; + std::map pairs; + }; + + REGISTER_DAY(2021, Day14, Data, int64_t); + + REGISTER_TEST_EXAMPLE(2021, Day14, ExampleInput, 1, 1588); + REGISTER_TEST(2021, Day14, Input, 1, 4517); + REGISTER_TEST_EXAMPLE(2021, Day14, ExampleInput, 2, 2188189693529); + REGISTER_TEST(2021, Day14, Input, 2, 4704817645083); + + READ_INPUT(input) + { + Data data; + getline(input, data.str); + std::string str; + getline(input, str); + + while (getline(input, str)) + { + data.pairs.emplace(str.substr(0, 2), str[6]); + } + return data; + } + + OUTPUT1(input) + { + std::string str = input.str; + for (int i = 0; i < 10; i++) + { + std::string newString; + for (size_t j = 0; j < str.size() - 1; j++) + { + auto it = input.pairs.find(str.substr(j, 2)); + newString.push_back(str[j]); + newString.push_back(it->second); + } + newString.push_back(str.back()); + str = newString; + } + std::map occurences; + for (char c : str) + { + occurences[c]++; + } + int64_t min = occurences.begin()->second; + char minChar = occurences.begin()->first; + int64_t max = occurences.begin()->second; + char maxChar = occurences.begin()->first; + for (auto& occurence : occurences) + { + if (occurence.second < min) + { + min = occurence.second; + minChar = occurence.first; + } + if (occurence.second > max) + { + max = occurence.second; + maxChar = occurence.first; + } + } + return occurences[maxChar] - occurences[minChar]; + } + + OUTPUT2(input) + { + std::string str = input.str; + std::map pairCounts; + for (size_t j = 0; j < str.size() - 1; j++) + { + pairCounts[input.str.substr(j, 2)]++; + } + for (int i = 0; i < 40; i++) + { + std::map cpy = pairCounts; + for (auto& pairCount : cpy) + { + if (pairCount.second <= 0) + continue; + auto it = input.pairs.find(pairCount.first); + std::string firstPair = pairCount.first.substr(0, 1) + it->second; + std::string secondPair = it->second + pairCount.first.substr(1, 1); + pairCounts[firstPair] += pairCount.second; + pairCounts[secondPair] += pairCount.second; + pairCounts[pairCount.first] -= pairCount.second; + } + } + std::map occurences; + for (auto& pairCount : pairCounts) + { + if (pairCount.second != 0) + occurences[pairCount.first[0]] += pairCount.second; + } + occurences[str.back()]++; + int64_t min = occurences.begin()->second; + char minChar = occurences.begin()->first; + int64_t max = occurences.begin()->second; + char maxChar = occurences.begin()->first; + for (auto& occurence : occurences) + { + if (occurence.second < min) + { + min = occurence.second; + minChar = occurence.first; + } + if (occurence.second > max) + { + max = occurence.second; + maxChar = occurence.first; + } + } + return occurences[maxChar] - occurences[minChar]; + } +} diff --git a/src/2021/Day15.cpp b/src/2021/Day15.cpp new file mode 100644 index 0000000..6f28287 --- /dev/null +++ b/src/2021/Day15.cpp @@ -0,0 +1,63 @@ +#include "common/aoc.h" + +namespace y2021::day15 +{ + + int Heuristic(const Array2D& array, const Index2D& index) + { + return std::abs(index.x - array.width + 1) && std::abs(index.y - array.height + 1); + } + + std::vector> Branch(const Array2D& array, const Index2D& index) + { + Index2D indices[4]{{index.x - 1, index.y}, {index.x, index.y - 1}, {index.x + 1, index.y}, {index.x, index.y + 1}}; + std::vector> moves; + for (auto& index : indices) + { + if (array.IsInside(index)) + moves.emplace_back(std::pair{array[index], index}); + } + return moves; + } + + int Goal(const Array2D& array, const Index2D& index) + { + return index.x == array.width - 1 && index.y == array.height - 1; + } + + int Pathfinder(const Array2D& array) + { + return Helper::AStar(array, Index2D{0, 0}, Heuristic, Branch, Goal); + } + + REGISTER_DAY(2021, Day15, Array2D, int); + + REGISTER_TEST_EXAMPLE(2021, Day15, ExampleInput, 1, 40); + REGISTER_TEST(2021, Day15, Input, 1, 714); + REGISTER_TEST_EXAMPLE(2021, Day15, ExampleInput, 2, 315); + REGISTER_TEST(2021, Day15, Input, 2, 2948); + + READ_INPUT(input) + { + return Input::ReadDigitsAsArray2D(input); + } + + OUTPUT1(input) + { + return Pathfinder(input); + } + + OUTPUT2(input) + { + Array2D array{input.width * 5, input.height * 5}; + Index2D index{0, 0}; + for (auto& val : array) + { + val = input.GetMod(index.x, index.y) + index.x / input.width + index.y / input.height; + if (val >= 10) + val = val % 10 + 1; + array.Increment(index); + } + return Pathfinder(array); + } +} diff --git a/src/2021/Day16.cpp b/src/2021/Day16.cpp new file mode 100644 index 0000000..1348867 --- /dev/null +++ b/src/2021/Day16.cpp @@ -0,0 +1,128 @@ +#include "common/aoc.h" + +namespace y2021::day16 +{ + int64_t Read(const std::string& str, int& offset, int size) + { + int64_t i = Helper::BinStrToInt64(str.substr(offset, size)); + offset += size; + return i; + } + + std::pair ReadPacket(const std::string& str, int& offset) + { + int64_t version = Read(str, offset, 3); + int64_t type = Read(str, offset, 3); + + if (type == 4) + { + // Normal packet + int64_t type = Read(str, offset, 1); + int64_t val = 0; + while (type == 1) + { + val = (val << 4) | Read(str, offset, 4); + type = Read(str, offset, 1); + } + val = (val << 4) | Read(str, offset, 4); + return {version, val}; + } + else + { + // Packet containing packets + int64_t lengthId = Read(str, offset, 1); + std::vector values; + int64_t versionTotal = version; + if (lengthId == 0) + { + int64_t totalLength = Read(str, offset, 15); + int64_t start = offset; + while (offset < start + totalLength) + { + auto [version, value] = ReadPacket(str, offset); + values.emplace_back(value); + versionTotal += version; + } + } + else + { + int64_t totalPackets = Read(str, offset, 11); + for (int64_t i = 0; i < totalPackets; i++) + { + auto [version, value] = ReadPacket(str, offset); + values.emplace_back(value); + versionTotal += version; + } + } + + if (type == 0) + return {versionTotal, + std::accumulate(values.begin(), values.end(), 0ll, [](int64_t sum, int64_t val) { return sum + val; })}; + else if (type == 1) + return { + versionTotal, + std::accumulate(values.begin(), values.end(), 1ll, [](int64_t prod, int64_t val) { return prod * val; })}; + else if (type == 2) + return { + versionTotal, + std::accumulate( + values.begin(), values.end(), values.front(), [](int64_t max, int64_t val) { return std::min(max, val); })}; + else if (type == 3) + return { + versionTotal, + std::accumulate( + values.begin(), values.end(), values.front(), [](int64_t min, int64_t val) { return std::max(min, val); })}; + else if (type == 5) + return {versionTotal, values[0] > values[1] ? 1 : 0}; + else if (type == 6) + return {versionTotal, values[0] < values[1] ? 1 : 0}; + else if (type == 7) + return {versionTotal, values[0] == values[1] ? 1 : 0}; + } + return {0, 0}; + } + + std::pair ReadPacket(const std::string& str) + { + int offset = 0; + return ReadPacket(str, offset); + } + + REGISTER_DAY(2021, Day16, std::string, int64_t); + + REGISTER_TEST_EXAMPLE(2021, Day16, ExampleInput, 1, 20); + REGISTER_TEST(2021, Day16, Input, 1, 984); + REGISTER_TEST_EXAMPLE(2021, Day16, ExampleInput, 2, 1); + REGISTER_TEST(2021, Day16, Input, 2, 1015320896946); + + READ_INPUT(input) + { + std::string str; + getline(input, str); + std::string bin; + for (char c : str) + { + if (c >= '0' && c <= '9') + { + char val = c - '0'; // vs compiler throws warnings if this is inlined into the bitset function... + bin += std::bitset<4>(val).to_string(); + } + else if (c >= 'A' && c <= 'F') + { + char val = c - 'A' + 10; // vs compiler throws warnings if this is inlined into the bitset function... + bin += std::bitset<4>(val).to_string(); + } + } + return bin; + } + + OUTPUT1(input) + { + return ReadPacket(input).first; + } + + OUTPUT2(input) + { + return ReadPacket(input).second; + } +} diff --git a/src/2021/Day17.cpp b/src/2021/Day17.cpp new file mode 100644 index 0000000..dcfeb1b --- /dev/null +++ b/src/2021/Day17.cpp @@ -0,0 +1,61 @@ +#include "common/aoc.h" + +namespace y2021::day17 +{ + struct Area + { + Index2D p1; + Index2D p2; + }; + + REGISTER_DAY(2021, Day17, Area, int); + + REGISTER_TEST_EXAMPLE(2021, Day17, ExampleInput, 1, 45); + REGISTER_TEST(2021, Day17, Input, 1, 2701); + REGISTER_TEST_EXAMPLE(2021, Day17, ExampleInput, 2, 112); + REGISTER_TEST(2021, Day17, Input, 2, 1070); + + READ_INPUT(input) + { + Area area; + input >> "target area: x=" >> area.p1.x >> ".." >> area.p2.x >> ", y=" >> area.p1.y >> ".." >> area.p2.y; + return area; + } + + OUTPUT1(input) + { + int v = -input.p1.y; + return v * (v - 1) / 2; + } + + OUTPUT2(input) + { + int count = 0; + for (int y = input.p1.y - 1; y <= -input.p1.y; y++) + { + for (int x = 1; x <= input.p2.x; x++) + { + int xPos = 0; + int yPos = 0; + + int vx = x; + int vy = y; + while (xPos <= input.p2.x && yPos >= input.p1.y) + { + if (xPos >= input.p1.x && xPos <= input.p2.x && yPos >= input.p1.y && yPos <= input.p2.y) + { + count++; + break; + } + xPos += vx; + yPos += vy; + vx--; + if (vx < 0) + vx = 0; + vy--; + } + } + } + return count; + } +} diff --git a/src/2021/Day18.cpp b/src/2021/Day18.cpp new file mode 100644 index 0000000..b0b07a5 --- /dev/null +++ b/src/2021/Day18.cpp @@ -0,0 +1,323 @@ +#include "common/aoc.h" + +namespace y2021::day18 +{ + struct Node + { + Node* parent = nullptr; + int value = 0; + std::vector nodes; + + Node() = default; + + Node(const std::string& str) + : nodes{Node{}, Node{}} + { + UpdateParents(); + std::stringstream ss{str}; + ss >> *this; + } + + Node(int value) + : value{value} + { + } + + Node(const Node& left, const Node& right) + : nodes{left, right} + { + UpdateParents(); + } + + Node& Left() + { + return nodes[0]; + } + + const Node& Left() const + { + return nodes[0]; + } + + Node& Right() + { + return nodes[1]; + } + + const Node& Right() const + { + return nodes[1]; + } + + bool IsNumber() const + { + return nodes.size() == 0; + } + + int& Number() + { + return value; + } + + int Number() const + { + return value; + } + + int Calculate() + { + int i = 0; + if (Left().IsNumber()) + i += 3 * Left().Number(); + else + i += 3 * Left().Calculate(); + + if (Right().IsNumber()) + i += 2 * Right().Number(); + else + i += 2 * Right().Calculate(); + return i; + } + + void AddToLeft(int value, Node* from) + { + if (Left().IsNumber()) + Left().Number() += value; + else + { + Node& node = Left(); + if (&node == from) + { + if (parent != nullptr) + parent->AddToLeft(value, this); + } + else + node.AddToRightDown(value); + } + } + + void AddToRightDown(int value) + { + if (Right().IsNumber()) + Right().Number() += value; + else + { + Right().AddToRightDown(value); + } + } + + void AddToRight(int value, Node* from) + { + if (Right().IsNumber()) + Right().Number() += value; + else + { + Node& node = Right(); + if (&node == from) + { + if (parent != nullptr) + parent->AddToRight(value, this); + } + else + node.AddToLeftDown(value); + } + } + + void AddToLeftDown(int value) + { + if (Left().IsNumber()) + Left().Number() += value; + else + { + Left().AddToLeftDown(value); + } + } + + bool Explode(int depth = 0) + { + if (depth >= 4) + { + parent->AddToLeft(Left().Number(), this); + parent->AddToRight(Right().Number(), this); + + if (!parent->Left().IsNumber() && &parent->Left() == this) + parent->Left() = 0; + if (!parent->Right().IsNumber() && &parent->Right() == this) + parent->Right() = 0; + return true; + } + if (!Left().IsNumber()) + if (Left().Explode(depth + 1)) + return true; + if (!Right().IsNumber()) + if (Right().Explode(depth + 1)) + return true; + return false; + } + + bool Split() + { + if (!Left().IsNumber()) + { + if (Left().Split()) + return true; + } + else if (Left().Number() >= 10) + { + int val = Left().Number(); + Node node{(val >> 1), (val >> 1) + (val % 2)}; + node.parent = this; + Left() = node; + return true; + } + + if (!Right().IsNumber()) + { + if (Right().Split()) + return true; + } + else if (Right().Number() >= 10) + { + int val = Right().Number(); + Node node{(val >> 1), (val >> 1) + (val % 2)}; + node.parent = this; + Right() = node; + return true; + } + return false; + } + + void UpdateParents() + { + if (!Left().IsNumber()) + { + Left().parent = this; + Left().UpdateParents(); + } + if (!Right().IsNumber()) + { + Right().parent = this; + Right().UpdateParents(); + } + } + + void Print(std::ostream& stream, int depth) const + { + std::vector depthStart{'|', '<', '{', '(', '['}; + std::vector depthEnd{'|', '>', '}', ')', ']'}; + stream << depthStart[depth]; + if (Left().IsNumber()) + stream << Left().Number(); + else + Left().Print(stream, depth + 1); + stream << ","; + if (Right().IsNumber()) + stream << Right().Number(); + else + Right().Print(stream, depth + 1); + stream << depthEnd[depth]; + } + + void operator=(int value) + { + nodes.clear(); + this->value = value; + } + + friend std::ostream& operator<<(std::ostream& stream, const Node& node) + { + node.Print(stream, 0); + return stream; + } + + friend std::istream& operator>>(std::istream& stream, Node& node) + { + stream >> "["; + if (stream.peek() == '[') + { + Node left{Node{}, Node{}}; + stream >> left; + left.parent = &node; + node.Left() = left; + } + else + { + int i; + stream >> i; + node.Left() = i; + } + stream >> ","; + if (stream.peek() == '[') + { + Node right{Node{}, Node{}}; + stream >> right; + right.parent = &node; + node.Right() = right; + } + else + { + int i; + stream >> i; + node.Right() = i; + } + stream >> "]"; + return stream; + } + }; + + Node Reduce(const Node& lhs, const Node& rhs) + { + Node node{lhs, rhs}; + bool changed = true; + while (changed) + { + changed = false; + while (node.Explode()) changed = true; + if (node.Split()) + changed = true; + } + return node; + } + + REGISTER_DAY(2021, Day18, std::vector, int); + + REGISTER_TEST_EXAMPLE(2021, Day18, ExampleInput, 1, 4140); + REGISTER_TEST(2021, Day18, Input, 1, 4137); + REGISTER_TEST_EXAMPLE(2021, Day18, ExampleInput, 2, 3993); + REGISTER_TEST(2021, Day18, Input, 2, 4573); + + READ_INPUT(input) + { + std::vector nodes; + std::string str; + while (getline(input, str)) + { + nodes.emplace_back(str); + } + return nodes; + } + + OUTPUT1(input) + { + Node node = input[0]; + for (size_t i = 1; i < input.size(); i++) + { + node = Reduce(node, input[i]); + } + return node.Calculate(); + } + + OUTPUT2(input) + { + int max = 0; + for (size_t i = 0; i < input.size() - 1; i++) + { + for (size_t j = 1; j < input.size(); j++) + { + max = std::max(max, Reduce(input[j], input[i]).Calculate()); + max = std::max(max, Reduce(input[i], input[j]).Calculate()); + } + } + return max; + } +} diff --git a/src/2021/Day19.cpp b/src/2021/Day19.cpp new file mode 100644 index 0000000..138e672 --- /dev/null +++ b/src/2021/Day19.cpp @@ -0,0 +1,278 @@ +#include "common/aoc.h" + +namespace y2021::day19 +{ + constexpr int MAX_ORIENTATIONS = 24; + constexpr int MIN_OVERLAP = 12; + + struct Scanner + { + std::set beacons; + }; + + struct Overlap + { + bool overlapping = false; + int orientation = 0; + Index3D offset{0, 0}; + + friend std::ostream& operator<<(std::ostream& stream, const Overlap& overlap) + { + if (overlap.overlapping) + return stream << "true " << overlap.orientation << " " << overlap.offset; + else + return stream << "false"; + } + + operator bool() + { + return overlapping; + } + }; + + Index3D RotateY(const Index3D& index) + { + return Index3D{-index.z, index.y, index.x}; + } + + Index3D RotateY2(const Index3D& index) + { + return Index3D{-index.x, index.y, -index.z}; + } + + Index3D RotateY3(const Index3D& index) + { + return Index3D{index.z, index.y, -index.x}; + } + + Index3D RotateX(const Index3D& index) + { + return Index3D{index.x, -index.z, index.y}; + } + + Index3D RotateX2(const Index3D& index) + { + return Index3D{index.x, -index.y, -index.z}; + } + + Index3D RotateX3(const Index3D& index) + { + return Index3D{index.x, index.z, -index.y}; + } + + Index3D RotateZ(const Index3D& index) + { + return Index3D{-index.y, index.x, index.z}; + } + + Index3D RotateZ2(const Index3D& index) + { + return Index3D{-index.x, -index.y, index.z}; + } + + Index3D RotateZ3(const Index3D& index) + { + return Index3D{index.y, -index.x, index.z}; + } + + Index3D Orient(const Index3D& index, int i) + { + switch (i) + { + // Facing forward + case 0: + return index; + case 1: + return RotateZ(index); + case 2: + return RotateZ2(index); + case 3: + return RotateZ3(index); + + // Facing left + case 4: + return RotateY(index); + case 5: + return RotateX(RotateY(index)); + case 6: + return RotateX2(RotateY(index)); + case 7: + return RotateX3(RotateY(index)); + + // Facing backward + case 8: + return RotateY2(index); + case 9: + return RotateZ(RotateY2(index)); + case 10: + return RotateZ2(RotateY2(index)); + case 11: + return RotateZ3(RotateY2(index)); + + // Facing right + case 12: + return RotateY3(index); + case 13: + return RotateX(RotateY3(index)); + case 14: + return RotateX2(RotateY3(index)); + case 15: + return RotateX3(RotateY3(index)); + + // Facing down + case 16: + return RotateX(index); + case 17: + return RotateY(RotateX(index)); + case 18: + return RotateY2(RotateX(index)); + case 19: + return RotateY3(RotateX(index)); + + // Facing up + case 20: + return RotateX3(index); + case 21: + return RotateY(RotateX3(index)); + case 22: + return RotateY2(RotateX3(index)); + case 23: + return RotateY3(RotateX3(index)); + } + std::cout << "Invalid orientation: " << i << std::endl; + return index; + } + + Overlap IsOverlapping(const std::set& layout, const Scanner& scanner) + { + for (auto& index : layout) + { + int beaconsLeft = scanner.beacons.size(); + for (auto it = scanner.beacons.begin(); it != scanner.beacons.end(); it++) + { + if (beaconsLeft < MIN_OVERLAP) + break; + beaconsLeft--; + + for (int i = 0; i < MAX_ORIENTATIONS; i++) + { + Index3D offset = index - Orient(*it, i); + int count = 0; + for (auto it2 = scanner.beacons.begin(); it2 != scanner.beacons.end(); it2++) + { + if (layout.count(offset + Orient(*it2, i))) + { + count++; + } + } + if (count >= MIN_OVERLAP) + return {true, i, offset}; + } + } + } + return {false, 0, Index3D{0, 0}}; + } + + std::pair Solve(std::vector scanners) + { + // Find first overlap to initialize the "correct" layout + std::set layout; + for (auto it = scanners.begin(); it != scanners.end(); it++) + { + bool found = false; + for (auto it2 = it; it2 != scanners.end(); it2++) + { + if (it == it2) + continue; + Overlap overlap = IsOverlapping(it->beacons, *it2); + if (overlap) + { + layout = it->beacons; + scanners.erase(it); + found = true; + break; + } + } + if (found) + break; + } + + // Find overlapping scanners one by one + std::vector scannerPos{Index3D{0, 0, 0}}; + while (!scanners.empty()) + { + bool removed = false; + for (auto it = scanners.begin(); it != scanners.end(); it++) + { + Overlap overlap = IsOverlapping(layout, *it); + if (overlap) + { + for (auto& beacon : it->beacons) + { + layout.emplace(overlap.offset + Orient(beacon, overlap.orientation)); + } + scannerPos.emplace_back(overlap.offset); + scanners.erase(it); + removed = true; + break; + } + } + if (!removed) + { + std::cout << "Could not find position of any remaining scanners" << std::endl; + break; + } + } + + // Find largest manhattan distance between two scanners + int maxDistance = 0; + for (size_t i = 0; i < scannerPos.size(); i++) + { + for (size_t j = i + 1; j < scannerPos.size(); j++) + { + Index3D distance = scannerPos[i] - scannerPos[j]; + maxDistance = std::max(maxDistance, std::abs(distance.x) + std::abs(distance.y) + std::abs(distance.z)); + } + } + return {layout.size(), maxDistance}; + } + + REGISTER_DAY(2021, Day19, std::vector, int); + + REGISTER_TEST_EXAMPLE(2021, Day19, ExampleInput, 1, 79); + REGISTER_TEST(2021, Day19, Input, 1, 359); + REGISTER_TEST_EXAMPLE(2021, Day19, ExampleInput, 2, 3621); + REGISTER_TEST(2021, Day19, Input, 2, 12292); + + READ_INPUT(input) + { + std::vector scanners; + std::string str; + while (getline(input, str)) + { + Scanner scanner; + getline(input, str); + while (!str.empty()) + { + std::stringstream ss{str}; + Index3D index; + ss >> index.x >> "," >> index.y >> "," >> index.z; + scanner.beacons.emplace(index); + str.clear(); + getline(input, str); + } + scanners.emplace_back(scanner); + } + return scanners; + } + + OUTPUT1(input) + { + return Solve(input).first; + } + + OUTPUT2(input) + { + return Solve(input).second; + } +} diff --git a/src/2021/Day20.cpp b/src/2021/Day20.cpp new file mode 100644 index 0000000..52a6dde --- /dev/null +++ b/src/2021/Day20.cpp @@ -0,0 +1,86 @@ +#include "common/aoc.h" + +namespace y2021::day20 +{ + struct Data + { + std::string line; + Array2D grid; + }; + + int GetNumber(const Array2D& array, Index2D index) + { + int num = 0; + for (int y = index.y - 1; y <= index.y + 1; y++) + { + for (int x = index.x - 1; x <= index.x + 1; x++) + { + num <<= 1; + Index2D newIndex{x, y}; + if (array.IsInside(newIndex) && array[newIndex] == '#') + num |= 1; + } + } + return num; + } + + int Solve(const Data& input, int iter) + { + int SIZE = (2 * iter) * 2; + Array2D output{input.grid.width + SIZE, input.grid.height + SIZE}; + Index2D index{0, 0}; + output.Fill('.'); + for (auto& val : input.grid) + { + output.Set(index.x + SIZE / 2, index.y + SIZE / 2, val); + input.grid.Increment(index); + } + for (int i = 0; i < iter; i++) + { + Array2D cpy = output; + Index2D index{0, 0}; + for (auto& grid : output) + { + int num = GetNumber(cpy, index); + output[index] = input.line[num]; + output.Increment(index); + } + std::string str; + } + + int count = 0; + for (int y = iter; y < output.height - iter; y++) + for (int x = iter; x < output.width - iter; x++) + if (output.Get(x, y) == '#') + count++; + + return count; + } + + REGISTER_DAY(2021, Day20, Data, int); + + REGISTER_TEST_EXAMPLE(2021, Day20, ExampleInput, 1, 35); + REGISTER_TEST(2021, Day20, Input, 1, 5203); + REGISTER_TEST_EXAMPLE(2021, Day20, ExampleInput, 2, 3351); + REGISTER_TEST(2021, Day20, Input, 2, 18806); + + READ_INPUT(input) + { + Data data; + std::getline(input, data.line); + std::string str; + getline(input, str); + data.grid = Input::ReadArray2D(input); + return data; + } + + OUTPUT1(input) + { + return Solve(input, 2); + } + + OUTPUT2(input) + { + return Solve(input, 50); + } +} diff --git a/src/2021/Day21.cpp b/src/2021/Day21.cpp new file mode 100644 index 0000000..c8c33d7 --- /dev/null +++ b/src/2021/Day21.cpp @@ -0,0 +1,107 @@ +#include "common/aoc.h" + +namespace y2021::day21 +{ + + int Mod(int i) + { + return (i - 1) % 10 + 1; + } + + std::pair Roll(int position1, int points1, int position2, int points2, bool player1, int depth) + { + if (points2 >= 21) + return {0, 1}; + else if (points1 >= 21) + return {1, 0}; + + // 3: 111 + // 4: 211 121 112 + // 5: 221 212 122 311 131 113 + // 6: 222 321 312 213 231 123 132 + // 7: 223 232 323 133 331 313 + // 8: 233 323 322 + // 9: 333 + std::map outcomes{{3, 1}, {4, 3}, {5, 6}, {6, 7}, {7, 6}, {8, 3}, {9, 1}}; + + std::pair wins{0, 0}; + int initialPos = 0; + int initialPoints = 0; + if (player1) + { + initialPos = position1; + initialPoints = points1; + } + else + { + initialPos = position2; + initialPoints = points2; + } + for (auto [value, times] : outcomes) + { + if (player1) + { + position1 = initialPos; + points1 = initialPoints; + position1 = Mod(position1 + value); + points1 += position1; + } + else + { + position2 = initialPos; + points2 = initialPoints; + position2 = Mod(position2 + value); + points2 += position2; + } + auto subwins = Roll(position1, points1, position2, points2, !player1, depth + 1); + wins.first += times * subwins.first; + wins.second += times * subwins.second; + } + return wins; + } + + REGISTER_DAY(2021, Day21, std::vector, int64_t); + + REGISTER_TEST_EXAMPLE(2021, Day21, ExampleInput, 1, 739785); + REGISTER_TEST(2021, Day21, Input, 1, 503478); + REGISTER_TEST_EXAMPLE(2021, Day21, ExampleInput, 2, 444356092776315); + REGISTER_TEST(2021, Day21, Input, 2, 716241959649754); + + READ_INPUT(input) + { + std::vector players; + int i; + std::string str; + while (getline(input, str)) + { + std::stringstream ss{str}; + ss >> "Player" >> i >> "starting position:" >> i; + players.emplace_back(i); + } + return players; + } + + OUTPUT1(input) + { + std::vector positions = input; + std::vector points; + points.resize(input.size()); + for (int i = 1; i < 10000; i += 3) + { + int index = (i - 1) % 2; + positions[index] = Mod(positions[index] + 3 * (i + 1)); + points[index] += positions[index]; + if (points[index] >= 1000) + { + return ((int64_t)i + 2ll) * (int64_t)points[(index + 1) % 2]; + } + } + return 0; + } + + OUTPUT2(input) + { + std::pair wins = Roll(input[0], 0, input[1], 0, true, 0); + return std::max(wins.first, wins.second); + } +} diff --git a/src/2021/Day22.cpp b/src/2021/Day22.cpp new file mode 100644 index 0000000..de73517 --- /dev/null +++ b/src/2021/Day22.cpp @@ -0,0 +1,138 @@ +#include "common/aoc.h" + +namespace y2021::day22 +{ + + struct Overlap; + + struct Volume + { + int sign = 0; + Index3D min = Index3D{0, 0}; + Index3D max = Index3D{0, 0}; + + Volume() + { + } + + Volume(int sign, const Index3D& min, const Index3D& max) + : sign{sign}, + min{min}, + max{max} + { + } + + int64_t GetCubes() const + { + return ((int64_t)max.x - (int64_t)min.x + 1ll) * ((int64_t)max.y - (int64_t)min.y + 1ll) * + ((int64_t)max.z - (int64_t)min.z + 1ll); + } + + Overlap GetOverlap(const Volume& other) const; + + friend std::ostream& operator<<(std::ostream& stream, const Volume& volume) + { + return stream << volume.min << " " << volume.max; + } + }; + + struct Overlap + { + bool overlap = false; + Volume volume; + + Overlap(bool overlap, const Volume& volume) + : overlap{overlap}, + volume{volume} + { + } + + operator bool() + { + return overlap; + } + }; + + Overlap Volume::GetOverlap(const Volume& other) const + { + if (min.x > other.max.x || min.y > other.max.y || min.z > other.max.z || max.x < other.min.x || + max.y < other.min.y || max.z < other.min.z) + return Overlap{false, Volume{0, Index3D{}, Index3D{}}}; + + return Overlap{ + true, + Volume{-other.sign, + Index3D{std::max(min.x, other.min.x), std::max(min.y, other.min.y), std::max(min.z, other.min.z)}, + Index3D{std::min(max.x, other.max.x), std::min(max.y, other.max.y), std::min(max.z, other.max.z)}}}; + } + + REGISTER_DAY(2021, Day22, std::vector, int64_t); + + REGISTER_TEST_EXAMPLE(2021, Day22, ExampleInput, 1, 474140); + REGISTER_TEST(2021, Day22, Input, 1, 596598); + REGISTER_TEST_EXAMPLE(2021, Day22, ExampleInput, 2, 2758514936282235); + REGISTER_TEST(2021, Day22, Input, 2, 1199121349148621); + + READ_INPUT(input) + { + std::vector volumes; + std::string str; + while (getline(input, str)) + { + Volume volume; + std::stringstream ss{str}; + std::string s; + ss >> s >> "x=" >> volume.min.x >> ".." >> volume.max.x >> "," >> "y=" >> volume.min.y >> ".." >> volume.max.y >> + "," >> "z=" >> volume.min.z >> ".." >> volume.max.z; + volume.sign = s == "on" ? 1 : -1; + volumes.emplace_back(volume); + } + return volumes; + } + + OUTPUT1(input) + { + Array3D values{101, 101, 101}; + values.Fill('.'); + for (auto& region : input) + { + for (int z = std::max(region.min.z, -50); z <= std::min(region.max.z, 50); z++) + { + for (int y = std::max(region.min.y, -50); y <= std::min(region.max.y, 50); y++) + { + for (int x = std::max(region.min.x, -50); x <= std::min(region.max.x, 50); x++) + { + if (values.IsInside(Index3D{x + 50, y + 50, z + 50})) + { + values.Set(x + 50, y + 50, z + 50, region.sign > 0 ? '#' : '.'); + } + } + } + } + } + return values.Count('#'); + } + + OUTPUT2(input) + { + std::vector volumes; + for (auto& volume : input) + { + size_t size = volumes.size(); + for (size_t i = 0; i < size; i++) + { + Overlap overlap = volume.GetOverlap(volumes[i]); + if (overlap) + { + volumes.emplace_back(overlap.volume); + } + } + if (volume.sign > 0) + { + volumes.emplace_back(volume); + } + } + return Helper::Sum(volumes, + [](int64_t sum, const Volume& vol) { return sum + vol.GetCubes() * vol.sign; }); + } +} diff --git a/src/2021/Day23.cpp b/src/2021/Day23.cpp new file mode 100644 index 0000000..ed369f3 --- /dev/null +++ b/src/2021/Day23.cpp @@ -0,0 +1,243 @@ +#include "common/aoc.h" + +namespace y2021::day23 +{ + + using State = std::map; + + bool Find(const State& state, const Index2D& pos, char c) + { + auto it = state.find(pos); + if (it != state.end()) + return it->second == c; + return false; + } + + bool ValidateState(int height, const State& state) + { + return Find(state, {2, 1}, 'A') && Find(state, {2, 2}, 'A') && Find(state, {4, 1}, 'B') && + Find(state, {4, 2}, 'B') && Find(state, {6, 1}, 'C') && Find(state, {6, 2}, 'C') && + Find(state, {8, 1}, 'D') && Find(state, {8, 2}, 'D'); + } + + int Heuristic(int height, const State& state) + { + int h = 0; + for (auto& piece : state) + { + // Cost to move piece to the top of the room + if (piece.second == 'A' && piece.first.x != 2) + h += std::abs(piece.first.x - 2) + std::abs(piece.first.y - 0) + 1; + if (piece.second == 'B' && piece.first.x != 4) + h += (std::abs(piece.first.x - 4) + std::abs(piece.first.y - 0) + 1) * 10; + if (piece.second == 'C' && piece.first.x != 6) + h += (std::abs(piece.first.x - 6) + std::abs(piece.first.y - 0) + 1) * 100; + if (piece.second == 'D' && piece.first.x != 8) + h += (std::abs(piece.first.x - 8) + std::abs(piece.first.y - 0) + 1) * 1000; + } + return h; + } + + int GetCost(char c) + { + switch (c) + { + case 'A': + return 1; + case 'B': + return 10; + case 'C': + return 100; + case 'D': + return 1000; + } + std::cout << "Invalid char: " << c << std::endl; + return 0; + } + + std::vector> ValidStates(int height, const State& state) + { + std::vector> newStates; + for (auto& piece : state) + { + if (piece.first.y > 0) + { // Piece is in the room, move to hallway + + // Check if already at correct position + bool canMove = false; + if (piece.first.x == (piece.second - 'A') * 2 + 2) + { + for (int i = piece.first.y + 1; i < 1 + height; i++) + { + if (state.find({piece.first.x, i})->second != piece.second) + { + canMove = true; + break; + } + } + } + else + canMove = true; + + // Check if piece is obstructed by others in the room + for (int i = piece.first.y - 1; i > 0; i--) + { + if (state.count({piece.first.x, i}) != 0) + { + canMove = false; + break; + } + } + + if (canMove) + { + // Check hallways to the left + for (int i = piece.first.x - 1; i >= 0; i--) + { + // Is valid position in hallway + if (i == 0 || i % 2 == 1 || i == 10) + { + if (state.count({i, 0}) == 0) + { + State newState = state; + newState[{i, 0}] = piece.second; + newState.erase(piece.first); + newStates.emplace_back(std::pair{ + (piece.first.y + std::abs(piece.first.x - i)) * GetCost(piece.second), newState}); + } + else + break; + } + } + + // Check hallways to the right + for (int i = piece.first.x + 1; i < 11; i++) + { + // Is valid position in hallway + if (i == 0 || i % 2 == 1 || i == 10) + { + if (state.count({i, 0}) == 0) + { + State newState = state; + newState[{i, 0}] = piece.second; + newState.erase(piece.first); + newStates.emplace_back(std::pair{ + (piece.first.y + std::abs(piece.first.x - i)) * GetCost(piece.second), newState}); + } + else + break; + } + } + } + } + else + { // Piece is in the hallway, move to room + + int x = (piece.second - 'A') * 2 + 2; + int dir = Helper::Sign(x - piece.first.x); + bool canMove = true; + + // Check if hallway is clear to correct position + for (int i = piece.first.x + dir; (dir < 0 && i > x) || (dir > 0 && i < x); i += dir) + { + if (state.count({i, 0}) != 0) + { + canMove = false; + break; + } + } + + if (canMove) + { + // Check if room is filled with same piece or no pieces + int pos = height; + for (int i = 1; i < 1 + height; i++) + { + if (state.count({x, i}) != 0) + { + if (state.find({x, i})->second == piece.second) + { + if (pos == height) + pos = i - 1; + } + else + { + pos = 0; + break; + } + } + } + if (pos != 0) + { + State newState = state; + newState[{x, pos}] = piece.second; + newState.erase(piece.first); + newStates.emplace_back( + std::pair{(pos + std::abs(piece.first.x - x)) * GetCost(piece.second), newState}); + } + } + } + } + return newStates; + } + + int Solve(const State& state, int h) + { + return Helper::AStar(h, state, Heuristic, ValidStates, ValidateState); + } + + REGISTER_DAY(2021, Day23, State, int); + + REGISTER_TEST_EXAMPLE(2021, Day23, ExampleInput, 1, 12521); + REGISTER_TEST(2021, Day23, Input, 1, 14350); + REGISTER_TEST_EXAMPLE(2021, Day23, ExampleInput, 2, 44169); + REGISTER_TEST(2021, Day23, Input, 2, 49742); + + READ_INPUT(input) + { + std::string str; + getline(input, str); + getline(input, str); + getline(input, str); + std::map state; + state.emplace(Index2D{2, 1}, str[3]); + state.emplace(Index2D{4, 1}, str[5]); + state.emplace(Index2D{6, 1}, str[7]); + state.emplace(Index2D{8, 1}, str[9]); + + getline(input, str); + state.emplace(Index2D{2, 2}, str[3]); + state.emplace(Index2D{4, 2}, str[5]); + state.emplace(Index2D{6, 2}, str[7]); + state.emplace(Index2D{8, 2}, str[9]); + + return state; + } + + OUTPUT1(input) + { + return Solve(input, 2); + } + + OUTPUT2(input) + { + State state; + for (auto& piece : input) + { + if (piece.first.y == 2) + state.emplace(Index2D{piece.first.x, 4}, piece.second); + else + state.emplace(piece.first, piece.second); + } + state.emplace(Index2D{2, 2}, 'D'); + state.emplace(Index2D{2, 3}, 'D'); + state.emplace(Index2D{4, 2}, 'C'); + state.emplace(Index2D{4, 3}, 'B'); + state.emplace(Index2D{6, 2}, 'B'); + state.emplace(Index2D{6, 3}, 'A'); + state.emplace(Index2D{8, 2}, 'A'); + state.emplace(Index2D{8, 3}, 'C'); + + return Solve(state, 4); + } +} diff --git a/src/2021/Day24.cpp b/src/2021/Day24.cpp new file mode 100644 index 0000000..0735112 --- /dev/null +++ b/src/2021/Day24.cpp @@ -0,0 +1,126 @@ +#include "common/aoc.h" + +namespace y2021::day24 +{ + + enum class Operation + { + Inp, + Add, + Mul, + Div, + Mod, + Eql + }; + + struct Instruction + { + Operation operation; + char dst; + std::string src; + }; + + int64_t Read(const std::string& str, const std::map& data) + { + if (str[0] >= 'a' && str[0] <= 'z') + { + auto it = data.find(str[0]); + if (it != data.end()) + return it->second; + return 0; + } + return atoll(str.c_str()); + }; + + int64_t Read(char c, const std::map& data) + { + auto it = data.find(c); + if (it != data.end()) + return it->second; + return 0; + } + + bool ValidateInput(const std::vector& instructions, const std::string& str) + { + int inputOffset = 0; + std::map data; + for (int i = 0; i < instructions.size(); i++) + { + switch (instructions[i].operation) + { + case Operation::Inp: + data[instructions[i].dst] = str[inputOffset++] - '0'; + break; + case Operation::Add: + data[instructions[i].dst] = Read(instructions[i].dst, data) + Read(instructions[i].src, data); + break; + case Operation::Mul: + data[instructions[i].dst] = Read(instructions[i].dst, data) * Read(instructions[i].src, data); + break; + case Operation::Div: + data[instructions[i].dst] = Read(instructions[i].dst, data) / Read(instructions[i].src, data); + break; + case Operation::Mod: + data[instructions[i].dst] = Read(instructions[i].dst, data) % Read(instructions[i].src, data); + break; + case Operation::Eql: + data[instructions[i].dst] = Read(instructions[i].dst, data) == Read(instructions[i].src, data) ? 1 : 0; + break; + default: + std::cout << "Invalid operation" << std::endl; + } + } + return data['z'] == 0; + } + + REGISTER_DAY(2021, Day24, std::vector, int64_t); + + REGISTER_TEST(2021, Day24, Input, 1, 99919765949498); + REGISTER_TEST(2021, Day24, Input, 2, 24913111616151); + + READ_INPUT(input) + { + std::vector instructions; + std::string str; + while (getline(input, str)) + { + Instruction instruction; + std::string_view sv(str.c_str(), 3); + if (sv == "inp") + instruction.operation = Operation::Inp; + else if (sv == "add") + instruction.operation = Operation::Add; + else if (sv == "mul") + instruction.operation = Operation::Mul; + else if (sv == "div") + instruction.operation = Operation::Div; + else if (sv == "mod") + instruction.operation = Operation::Mod; + else if (sv == "eql") + instruction.operation = Operation::Eql; + else + std::cout << "Invalid operation: " << sv << std::endl; + instruction.dst = str[4]; + if (instruction.operation != Operation::Inp) + instruction.src = str.substr(6); + instructions.emplace_back(instruction); + } + return instructions; + } + + OUTPUT1(input) + { + std::string str = "99919765949498"; + if (ValidateInput(input, str)) + return atoll(str.c_str()); + return 0; + } + + OUTPUT2(input) + { + std::string str = "24913111616151"; + if (ValidateInput(input, str)) + return atoll(str.c_str()); + return 0; + } +} diff --git a/src/2021/Day25.cpp b/src/2021/Day25.cpp new file mode 100644 index 0000000..1f09f58 --- /dev/null +++ b/src/2021/Day25.cpp @@ -0,0 +1,66 @@ +#include "common/aoc.h" + +namespace y2021::day25 +{ + REGISTER_DAY(2021, Day25, Array2D, int); + + REGISTER_TEST_EXAMPLE(2021, Day25, ExampleInput, 1, 58); + REGISTER_TEST(2021, Day25, Input, 1, 419); + + READ_INPUT(input) + { + return Input::ReadArray2D(input); + } + + OUTPUT1(inputConst) + { + Array2D input = inputConst; + bool hasMoved = true; + int i = 0; + while (hasMoved) + { + hasMoved = false; + Array2D cpy = input; + Index2D index{0, 0}; + for (auto& c : cpy) + { + if (c == '>') + { + Index2D newIndex = index; + newIndex.x = (newIndex.x + 1) % cpy.width; + if (cpy[newIndex] == '.') + { + input[newIndex] = '>'; + input[index] = '.'; + hasMoved = true; + } + } + cpy.Increment(index); + } + cpy = input; + index = {0, 0}; + for (auto& c : cpy) + { + if (c == 'v') + { + Index2D newIndex = index; + newIndex.y = (newIndex.y + 1) % cpy.height; + if (cpy[newIndex] == '.') + { + input[newIndex] = 'v'; + input[index] = '.'; + hasMoved = true; + } + } + cpy.Increment(index); + } + i++; + } + return i; + } + + OUTPUT2(input) + { + return 0; + } +}