Add 2023 solutions
This commit is contained in:
@@ -0,0 +1,7 @@
|
|||||||
|
two1nine
|
||||||
|
eightwothree
|
||||||
|
abcone2threexyz
|
||||||
|
xtwone3four
|
||||||
|
4nineeightseven2
|
||||||
|
zoneight234
|
||||||
|
7pqrstsixteen
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
467..114..
|
||||||
|
...*......
|
||||||
|
..35..633.
|
||||||
|
......#...
|
||||||
|
617*......
|
||||||
|
.....+.58.
|
||||||
|
..592.....
|
||||||
|
......755.
|
||||||
|
...$.*....
|
||||||
|
.664.598..
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
Time: 7 15 30
|
||||||
|
Distance: 9 40 200
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
32T3K 765
|
||||||
|
T55J5 684
|
||||||
|
KK677 28
|
||||||
|
KTJJT 220
|
||||||
|
QQQJA 483
|
||||||
@@ -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)
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
0 3 6 9 12 15
|
||||||
|
1 3 6 10 15 21
|
||||||
|
10 13 16 21 30 45
|
||||||
@@ -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...
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
...#......
|
||||||
|
.......#..
|
||||||
|
#.........
|
||||||
|
..........
|
||||||
|
......#...
|
||||||
|
.#........
|
||||||
|
.........#
|
||||||
|
..........
|
||||||
|
.......#..
|
||||||
|
#...#.....
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
???.### 1,1,3
|
||||||
|
.??..??...?##. 1,1,3
|
||||||
|
?#?#?#?#?#?#?#? 1,3,1,6
|
||||||
|
????.#...#... 4,1,1
|
||||||
|
????.######..#####. 1,6,5
|
||||||
|
?###???????? 3,2,1
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#.##..##.
|
||||||
|
..#.##.#.
|
||||||
|
##......#
|
||||||
|
##......#
|
||||||
|
..#.##.#.
|
||||||
|
..##..##.
|
||||||
|
#.#.##.#.
|
||||||
|
|
||||||
|
#...##..#
|
||||||
|
#....#..#
|
||||||
|
..##..###
|
||||||
|
#####.##.
|
||||||
|
#####.##.
|
||||||
|
..##..###
|
||||||
|
#....#..#
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
O....#....
|
||||||
|
O.OO#....#
|
||||||
|
.....##...
|
||||||
|
OO.#O....O
|
||||||
|
.O.....O#.
|
||||||
|
O.#..O.#.#
|
||||||
|
..O..#O..O
|
||||||
|
.......O..
|
||||||
|
#....###..
|
||||||
|
#OO..#....
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
rn=1,cm-,qp=3,cm=2,qp-,pc=4,ot=9,ab=5,pc-,pc=6,ot=7
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
.|...\....
|
||||||
|
|.-.\.....
|
||||||
|
.....|-...
|
||||||
|
........|.
|
||||||
|
..........
|
||||||
|
.........\
|
||||||
|
..../.\\..
|
||||||
|
.-.-/..|..
|
||||||
|
.|....-|.\
|
||||||
|
..//.|....
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
2413432311323
|
||||||
|
3215453535623
|
||||||
|
3255245654254
|
||||||
|
3446585845452
|
||||||
|
4546657867536
|
||||||
|
1438598798454
|
||||||
|
4457876987766
|
||||||
|
3637877979653
|
||||||
|
4654967986887
|
||||||
|
4564679986453
|
||||||
|
1224686865563
|
||||||
|
2546548887735
|
||||||
|
4322674655533
|
||||||
@@ -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)
|
||||||
@@ -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}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
broadcaster -> a
|
||||||
|
%a -> inv, con
|
||||||
|
&inv -> b
|
||||||
|
%b -> con
|
||||||
|
&con -> output
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
...........
|
||||||
|
......##.#.
|
||||||
|
.###..#..#.
|
||||||
|
..#.#...#..
|
||||||
|
....#.#....
|
||||||
|
.....S.....
|
||||||
|
.##......#.
|
||||||
|
.......##..
|
||||||
|
.##.#.####.
|
||||||
|
.##...#.##.
|
||||||
|
...........
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
#.#####################
|
||||||
|
#.......#########...###
|
||||||
|
#######.#########.#.###
|
||||||
|
###.....#.>.>.###.#.###
|
||||||
|
###v#####.#v#.###.#.###
|
||||||
|
###.>...#.#.#.....#...#
|
||||||
|
###v###.#.#.#########.#
|
||||||
|
###...#.#.#.......#...#
|
||||||
|
#####.#.#.#######.#.###
|
||||||
|
#.....#.#.#.......#...#
|
||||||
|
#.#####.#.#.#########v#
|
||||||
|
#.#...#...#...###...>.#
|
||||||
|
#.#.#v#######v###.###v#
|
||||||
|
#...#.>.#...>.>.#.###.#
|
||||||
|
#####v#.#.###v#.#.###.#
|
||||||
|
#.....#...#...#.#.#...#
|
||||||
|
#.#########.###.#.#.###
|
||||||
|
#...###...#...#...#.###
|
||||||
|
###.###.#.###v#####v###
|
||||||
|
#...#...#.#.>.>.#.>.###
|
||||||
|
#.###.###.#.###.#.#v###
|
||||||
|
#.....###...###...#...#
|
||||||
|
#####################.#
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
#include "common/aoc.h"
|
||||||
|
|
||||||
|
namespace y2023::day01
|
||||||
|
{
|
||||||
|
REGISTER_DAY(2023, Day01, std::vector<std::string>, 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<std::string> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<std::vector<Set>>, 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<std::vector<Set>> games;
|
||||||
|
while (std::getline(input, str))
|
||||||
|
{
|
||||||
|
std::vector<Set> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
#include "common/aoc.h"
|
||||||
|
|
||||||
|
namespace y2023::day03
|
||||||
|
{
|
||||||
|
REGISTER_DAY(2023, Day03, Array2D<char>, 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<char>& 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<char>& 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<char>& 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<char>& array, const Index2D& index)
|
||||||
|
{
|
||||||
|
if (array.Get(index) == '*')
|
||||||
|
{
|
||||||
|
sum += GetGearRatio(input, index);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
#include "common/aoc.h"
|
||||||
|
|
||||||
|
namespace y2023::day04
|
||||||
|
{
|
||||||
|
|
||||||
|
struct Game
|
||||||
|
{
|
||||||
|
std::vector<int> winning;
|
||||||
|
std::set<int> numbers;
|
||||||
|
};
|
||||||
|
|
||||||
|
REGISTER_DAY(2023, Day04, std::vector<Game>, 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<Game> 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<int> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<int64_t> seeds;
|
||||||
|
std::vector<std::map<int64_t, Range>> 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<int64_t, Range>& 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<int64_t, Range>& 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<int64_t, Range> 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<int64_t, Range>& toRanges, std::vector<Range>& 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<Range> 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<Range> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
#include "common/aoc.h"
|
||||||
|
|
||||||
|
namespace y2023::day06
|
||||||
|
{
|
||||||
|
using Game = std::pair<int, int>;
|
||||||
|
REGISTER_DAY(2023, Day06, std::vector<Game>, 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<std::pair<int, int>> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
#include "common/aoc.h"
|
||||||
|
|
||||||
|
namespace y2023::day07
|
||||||
|
{
|
||||||
|
int CalcStrength(const std::string& hand)
|
||||||
|
{
|
||||||
|
std::map<char, int> 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<Hand>, 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<Hand> 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<Hand> 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<Hand> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
#include "common/aoc.h"
|
||||||
|
|
||||||
|
namespace y2023::day08
|
||||||
|
{
|
||||||
|
struct Map
|
||||||
|
{
|
||||||
|
std::string instruction;
|
||||||
|
std::string start;
|
||||||
|
std::map<std::string, std::pair<std::string, std::string>> 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<int> 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<std::string, int64_t> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
#include "common/aoc.h"
|
||||||
|
|
||||||
|
namespace y2023::day09
|
||||||
|
{
|
||||||
|
REGISTER_DAY(2023, Day09, std::vector<std::vector<int>>, 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<std::vector<int>> ints;
|
||||||
|
while (std::getline(input, str))
|
||||||
|
{
|
||||||
|
std::vector<int> 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<int>& numbers)
|
||||||
|
{
|
||||||
|
for (auto& number : numbers)
|
||||||
|
{
|
||||||
|
if (number != 0)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int> GetDifference(const std::vector<int>& numbers)
|
||||||
|
{
|
||||||
|
std::vector<int> 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<int>& numbers)
|
||||||
|
{
|
||||||
|
if (AllZeros(numbers))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return numbers.back() + GetExtrapolateForward(GetDifference(numbers));
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetExtrapolateBackward(const std::vector<int>& 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,195 @@
|
|||||||
|
#include "common/aoc.h"
|
||||||
|
|
||||||
|
namespace y2023::day10
|
||||||
|
{
|
||||||
|
REGISTER_DAY(2023, Day10, Array2D<char>, 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<char>& 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<Index2D> GetPath(const Array2D<char>& 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<char> south{'|', 'L', 'J'};
|
||||||
|
std::set<char> north{'|', 'F', '7'};
|
||||||
|
std::set<char> west{'-', 'F', 'L'};
|
||||||
|
std::set<char> east{'-', 'J', '7'};
|
||||||
|
|
||||||
|
std::vector<char> 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<Index2D> 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<char>& map, Index2D pos)
|
||||||
|
{
|
||||||
|
std::stack<Index2D> 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<Index2D> path = GetPath(input);
|
||||||
|
Array2D<char> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
#include "common/aoc.h"
|
||||||
|
|
||||||
|
namespace y2023::day11
|
||||||
|
{
|
||||||
|
REGISTER_DAY(2023, Day11, Array2D<char>, 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<char>& input, int expansion)
|
||||||
|
{
|
||||||
|
std::set<int> vertEmpty;
|
||||||
|
std::set<int> 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<Index2D> galaxies;
|
||||||
|
input.Each(
|
||||||
|
[&](const Array2D<char>& 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
#include "common/aoc.h"
|
||||||
|
|
||||||
|
namespace y2023::day12
|
||||||
|
{
|
||||||
|
struct Record
|
||||||
|
{
|
||||||
|
std::string str;
|
||||||
|
std::vector<int> 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<Record>, 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<Record> 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<State, int64_t>& 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<State, int64_t> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
#include "common/aoc.h"
|
||||||
|
|
||||||
|
namespace y2023::day13
|
||||||
|
{
|
||||||
|
REGISTER_DAY(2023, Day13, std::vector<Array2D<char>>, 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<Array2D<char>> maps;
|
||||||
|
|
||||||
|
Array2D<char> array = Input::ReadArray2D(input);
|
||||||
|
while (array.width != 0)
|
||||||
|
{
|
||||||
|
maps.emplace_back(array);
|
||||||
|
array = Input::ReadArray2D(input);
|
||||||
|
}
|
||||||
|
return maps;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsRowRecleted(const Array2D<char>& 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<char>& 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<char>& map, int x)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < map.height; i++)
|
||||||
|
{
|
||||||
|
if (!IsRowRecleted(map, x, i))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsReflectedHorizontal(const Array2D<char>& map, int y)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < map.width; i++)
|
||||||
|
{
|
||||||
|
if (!IsColumnRecleted(map, i, y))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Index2D GetReflection(const Array2D<char>& 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<char> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
#include "common/aoc.h"
|
||||||
|
|
||||||
|
namespace y2023::day14
|
||||||
|
{
|
||||||
|
REGISTER_DAY(2023, Day14, Array2D<char>, 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<char>& 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<char>& 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<char>& 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<char>& 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<char>& 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<char> map = input;
|
||||||
|
North(map);
|
||||||
|
return CalculateLoad(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
OUTPUT2(input)
|
||||||
|
{
|
||||||
|
Array2D<char> map = input;
|
||||||
|
std::vector<int> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
#include "common/aoc.h"
|
||||||
|
|
||||||
|
namespace y2023::day15
|
||||||
|
{
|
||||||
|
REGISTER_DAY(2023, Day15, std::vector<std::string>, 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<std::string> 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<std::vector<std::pair<std::string, int>>> 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<std::string, int>& 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
#include "common/aoc.h"
|
||||||
|
|
||||||
|
namespace y2023::day16
|
||||||
|
{
|
||||||
|
struct Ray
|
||||||
|
{
|
||||||
|
Index2D pos;
|
||||||
|
Index2D vel;
|
||||||
|
};
|
||||||
|
|
||||||
|
REGISTER_DAY(2023, Day16, Array2D<char>, 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<char>& map, const Ray& ray)
|
||||||
|
{
|
||||||
|
Array2D<char> energized{map.width, map.height, '.'};
|
||||||
|
std::stack<Ray> rays;
|
||||||
|
rays.emplace(ray);
|
||||||
|
std::map<Index2D, bool> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<std::pair<int, State>> Branch(const Array2D<int>& map, const State& state)
|
||||||
|
{
|
||||||
|
std::vector<std::pair<int, State>> 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<std::pair<int, State>> Branch2(const Array2D<int>& map, const State& state)
|
||||||
|
{
|
||||||
|
std::vector<std::pair<int, State>> 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<int>& map, const State& state)
|
||||||
|
{
|
||||||
|
return state.position.x == map.width - 1 && state.position.y == map.height - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Goal2(const Array2D<int>& 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>, 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<State>{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<State>{startRight, startDown}, Branch2, Goal2);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<Dig>, 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<Dig> 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<Dig>& digs)
|
||||||
|
{
|
||||||
|
Index2D pos{0, 0};
|
||||||
|
std::vector<Index2D> 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<Dig> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<Rule> rules;
|
||||||
|
std::string otherwise;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Instructions
|
||||||
|
{
|
||||||
|
std::map<std::string, Rules> rules;
|
||||||
|
std::vector<std::map<char, int>> 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<char, int>{{'x', x}, {'m', m}, {'a', a}, {'s', s}});
|
||||||
|
}
|
||||||
|
|
||||||
|
return instructions;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Accept(const Instructions& ins, const std::map<char, int>& 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<char, std::pair<int, int>> 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<char, std::pair<int, int>> newData = data;
|
||||||
|
newData[rule.c].second = rule.i - 1;
|
||||||
|
sum += Accept2(ins, newData, rule.res);
|
||||||
|
data[rule.c].first = rule.i;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::map<char, std::pair<int, int>> 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<Gate, std::vector<std::string>>;
|
||||||
|
|
||||||
|
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<Gate, std::vector<std::string>> gates;
|
||||||
|
while (std::getline(input, str))
|
||||||
|
{
|
||||||
|
std::stringstream ss{str};
|
||||||
|
Gate gate;
|
||||||
|
ss >> gate.op >> gate.name >> "->";
|
||||||
|
std::vector<std::string> target;
|
||||||
|
while (std::getline(ss, str, ','))
|
||||||
|
{
|
||||||
|
target.emplace_back(str.substr(1));
|
||||||
|
}
|
||||||
|
gates.emplace(gate, target);
|
||||||
|
}
|
||||||
|
return gates;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct State
|
||||||
|
{
|
||||||
|
std::map<std::string, bool> flipFlopStates;
|
||||||
|
std::map<std::string, std::map<std::string, bool>> conjunctionStates;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Signal
|
||||||
|
{
|
||||||
|
std::string from;
|
||||||
|
std::string to;
|
||||||
|
bool high;
|
||||||
|
};
|
||||||
|
|
||||||
|
State InitiateState(const Gates& gates)
|
||||||
|
{
|
||||||
|
State state;
|
||||||
|
for (auto& gate : gates)
|
||||||
|
{
|
||||||
|
std::map<std::string, bool> 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<std::string>& targets,
|
||||||
|
std::queue<Signal>& 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<std::string>& targets,
|
||||||
|
std::queue<Signal>& 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<std::string>& targets, std::queue<Signal>& 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<std::pair<int, int>>& 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<std::string, std::pair<int, int>>& gateLoops, const std::set<std::string>& neededGates)
|
||||||
|
{
|
||||||
|
if (gateLoops.size() != neededGates.size())
|
||||||
|
return false;
|
||||||
|
for (auto& loop : gateLoops)
|
||||||
|
{
|
||||||
|
if (loop.second.second == 0)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::set<std::string> GetGatesToTarget(const Gates& gates, const std::string& target)
|
||||||
|
{
|
||||||
|
std::set<std::string> 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<int, int> signals{0, 0};
|
||||||
|
for (int i = 0; i < 1000; i++)
|
||||||
|
{
|
||||||
|
std::queue<Signal> 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<std::string> gateToRx = GetGatesToTarget(input, "rx"); // will only be 1 source gate
|
||||||
|
std::set<std::string> neededGates = GetGatesToTarget(input, *gateToRx.begin());
|
||||||
|
|
||||||
|
std::map<std::string, std::pair<int, int>> loops;
|
||||||
|
int i = 0;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
std::queue<Signal> 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<std::pair<int, int>> result;
|
||||||
|
for (auto& loop : loops)
|
||||||
|
{
|
||||||
|
result.emplace_back(loop.second);
|
||||||
|
}
|
||||||
|
return CalculateSolution(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
#include "common/aoc.h"
|
||||||
|
|
||||||
|
namespace y2023::day21
|
||||||
|
{
|
||||||
|
REGISTER_DAY(2023, Day21, Array2D<char>, 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<char>& map, Index2D start, int maxSteps)
|
||||||
|
{
|
||||||
|
if (maxSteps < 0)
|
||||||
|
return 0;
|
||||||
|
std::set<Index2D> exactly;
|
||||||
|
std::set<Index2D> visited;
|
||||||
|
std::queue<std::pair<Index2D, int>> stack{};
|
||||||
|
stack.push(std::make_pair(start, 0));
|
||||||
|
|
||||||
|
while (!stack.empty())
|
||||||
|
{
|
||||||
|
std::pair<Index2D, int> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<Brick>, 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<Brick> 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<Index3D, int>& 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<Index3D, int>& 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<Index3D, int>& 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<Brick>& bricks, const std::map<Index3D, int>& occupied)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < bricks.size(); i++)
|
||||||
|
{
|
||||||
|
if (CanMove(bricks[i], occupied, brickIndex))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Settle(std::vector<Brick>& bricks, std::map<Index3D, int>& 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<Index3D, int> occupied;
|
||||||
|
std::vector<Brick> 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<Brick> cpy = input;
|
||||||
|
std::map<Index3D, int> occupied;
|
||||||
|
Settle(cpy, occupied);
|
||||||
|
|
||||||
|
int sum = 0;
|
||||||
|
for (int i = 0; i < cpy.size(); i++)
|
||||||
|
{
|
||||||
|
std::map<Index3D, int> occupied;
|
||||||
|
std::set<Brick> handled;
|
||||||
|
std::vector<Brick> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,176 @@
|
|||||||
|
#include "common/aoc.h"
|
||||||
|
|
||||||
|
namespace y2023::day23
|
||||||
|
{
|
||||||
|
REGISTER_DAY(2023, Day23, Array2D<char>, 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<Index2D> visited;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct State2
|
||||||
|
{
|
||||||
|
Index2D pos;
|
||||||
|
int cost;
|
||||||
|
|
||||||
|
Index2D lastIntersection;
|
||||||
|
int lastIntersectionCost;
|
||||||
|
std::set<Index2D> visited;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DP
|
||||||
|
{
|
||||||
|
std::set<Index2D> 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<Index2D>::iterator it1 = visited.begin();
|
||||||
|
std::set<Index2D>::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<Index2D> GetIntersections(const Array2D<char>& map)
|
||||||
|
{
|
||||||
|
std::set<Index2D> 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<char>& map,
|
||||||
|
Graph<Index2D, int>& graph,
|
||||||
|
Index2D from,
|
||||||
|
const std::set<Index2D>& intersections)
|
||||||
|
{
|
||||||
|
for (auto& neighbor : map.GetNeighbors(from, false))
|
||||||
|
{
|
||||||
|
if (map.Get(neighbor) == '#')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::set<Index2D> 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<char>& map,
|
||||||
|
Graph<Index2D, int>& graph,
|
||||||
|
Index2D from,
|
||||||
|
const std::set<Index2D>& intersections)
|
||||||
|
{
|
||||||
|
for (auto& neighbor : map.GetNeighbors(from, false))
|
||||||
|
{
|
||||||
|
if (map.Get(neighbor) == '#')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::set<Index2D> 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<Index2D, int> graph;
|
||||||
|
std::set<Index2D> 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<Index2D, int> graph;
|
||||||
|
std::set<Index2D> 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});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<Wind>, 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<Wind> 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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
#include "common/aoc.h"
|
||||||
|
|
||||||
|
namespace y2023::day25
|
||||||
|
{
|
||||||
|
using Input = Graph<std::string, int>;
|
||||||
|
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<std::string, int> 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<bool, int> FloodFill(const Graph<std::string, int>& graph,
|
||||||
|
const std::set<std::pair<std::string, std::string>>& exclude)
|
||||||
|
{
|
||||||
|
std::set<std::string> visited;
|
||||||
|
visited.emplace(*graph.GetNodes().begin());
|
||||||
|
std::stack<std::string> 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<std::pair<std::string, std::string>, 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<int, std::vector<std::string>> paths = input.DijkstrasWithPath(*it1, *it2);
|
||||||
|
for (int j = 0; j < paths.second.size() - 1; j++)
|
||||||
|
{
|
||||||
|
std::pair<std::string, std::string> 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<std::pair<std::string, std::string>> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <bitset>
|
#include <bitset>
|
||||||
|
#include <cmath>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|||||||
Reference in New Issue
Block a user