Add 2023 solutions

This commit is contained in:
Thraix
2025-11-26 22:43:11 +01:00
parent be113565fb
commit 908b362ee5
51 changed files with 3571 additions and 0 deletions
+7
View File
@@ -0,0 +1,7 @@
two1nine
eightwothree
abcone2threexyz
xtwone3four
4nineeightseven2
zoneight234
7pqrstsixteen
+5
View File
@@ -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
+10
View File
@@ -0,0 +1,10 @@
467..114..
...*......
..35..633.
......#...
617*......
.....+.58.
..592.....
......755.
...$.*....
.664.598..
+6
View File
@@ -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
+33
View File
@@ -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
+2
View File
@@ -0,0 +1,2 @@
Time: 7 15 30
Distance: 9 40 200
+5
View File
@@ -0,0 +1,5 @@
32T3K 765
T55J5 684
KK677 28
KTJJT 220
QQQJA 483
+10
View File
@@ -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)
+3
View File
@@ -0,0 +1,3 @@
0 3 6 9 12 15
1 3 6 10 15 21
10 13 16 21 30 45
+10
View File
@@ -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...
+10
View File
@@ -0,0 +1,10 @@
...#......
.......#..
#.........
..........
......#...
.#........
.........#
..........
.......#..
#...#.....
+6
View File
@@ -0,0 +1,6 @@
???.### 1,1,3
.??..??...?##. 1,1,3
?#?#?#?#?#?#?#? 1,3,1,6
????.#...#... 4,1,1
????.######..#####. 1,6,5
?###???????? 3,2,1
+15
View File
@@ -0,0 +1,15 @@
#.##..##.
..#.##.#.
##......#
##......#
..#.##.#.
..##..##.
#.#.##.#.
#...##..#
#....#..#
..##..###
#####.##.
#####.##.
..##..###
#....#..#
+10
View File
@@ -0,0 +1,10 @@
O....#....
O.OO#....#
.....##...
OO.#O....O
.O.....O#.
O.#..O.#.#
..O..#O..O
.......O..
#....###..
#OO..#....
+1
View File
@@ -0,0 +1 @@
rn=1,cm-,qp=3,cm=2,qp-,pc=4,ot=9,ab=5,pc-,pc=6,ot=7
+10
View File
@@ -0,0 +1,10 @@
.|...\....
|.-.\.....
.....|-...
........|.
..........
.........\
..../.\\..
.-.-/..|..
.|....-|.\
..//.|....
+13
View File
@@ -0,0 +1,13 @@
2413432311323
3215453535623
3255245654254
3446585845452
4546657867536
1438598798454
4457876987766
3637877979653
4654967986887
4564679986453
1224686865563
2546548887735
4322674655533
+14
View File
@@ -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)
+17
View File
@@ -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}
+5
View File
@@ -0,0 +1,5 @@
broadcaster -> a
%a -> inv, con
&inv -> b
%b -> con
&con -> output
+11
View File
@@ -0,0 +1,11 @@
...........
......##.#.
.###..#..#.
..#.#...#..
....#.#....
.....S.....
.##......#.
.......##..
.##.#.####.
.##...#.##.
...........
+7
View File
@@ -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
+23
View File
@@ -0,0 +1,23 @@
#.#####################
#.......#########...###
#######.#########.#.###
###.....#.>.>.###.#.###
###v#####.#v#.###.#.###
###.>...#.#.#.....#...#
###v###.#.#.#########.#
###...#.#.#.......#...#
#####.#.#.#######.#.###
#.....#.#.#.......#...#
#.#####.#.#.#########v#
#.#...#...#...###...>.#
#.#.#v#######v###.###v#
#...#.>.#...>.>.#.###.#
#####v#.#.###v#.#.###.#
#.....#...#...#.#.#...#
#.#########.###.#.#.###
#...###...#...#...#.###
###.###.#.###v#####v###
#...#...#.#.>.>.#.>.###
#.###.###.#.###.#.#v###
#.....###...###...#...#
#####################.#
+5
View File
@@ -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
+13
View File
@@ -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
+95
View File
@@ -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;
}
}
+88
View File
@@ -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;
}
}
+116
View File
@@ -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;
}
}
+89
View File
@@ -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;
}
}
+157
View File
@@ -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;
}
}
+69
View File
@@ -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;
}
}
+155
View File
@@ -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;
}
}
+83
View File
@@ -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;
}
}
+83
View File
@@ -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;
}
}
+195
View File
@@ -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;
}
}
+95
View File
@@ -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);
}
}
+120
View File
@@ -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;
}
}
+137
View File
@@ -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;
}
}
+161
View File
@@ -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;
}
}
+89
View File
@@ -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;
}
}
+117
View File
@@ -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;
}
}
+103
View File
@@ -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);
}
}
+105
View File
@@ -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);
}
}
+151
View File
@@ -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");
}
}
+267
View File
@@ -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);
}
}
+98
View File
@@ -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;
}
}
+169
View File
@@ -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;
}
}
+176
View File
@@ -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});
}
}
+284
View File
@@ -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]);
}
}
+117
View File
@@ -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;
}
}
+1
View File
@@ -2,6 +2,7 @@
#include <algorithm>
#include <bitset>
#include <cmath>
#include <cstring>
#include <fstream>
#include <functional>