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 <bitset>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
|
||||
Reference in New Issue
Block a user