Initial commit with solutions from 2024

This commit is contained in:
Thraix
2025-11-26 21:59:50 +01:00
commit be113565fb
72 changed files with 7030 additions and 0 deletions
+59
View File
@@ -0,0 +1,59 @@
#include "common/aoc.h"
namespace day01
{
using InputType = std::pair<std::vector<int>, std::vector<int>>;
REGISTER_DAY(2024, Day01, InputType, int);
REGISTER_TEST_EXAMPLE(2024, Day01, ExampleInput, 1, 11);
REGISTER_TEST(2024, Day01, Input, 1, 1834060);
REGISTER_TEST_EXAMPLE(2024, Day01, ExampleInput, 2, 31);
REGISTER_TEST(2024, Day01, Input, 2, 21607792);
READ_INPUT(input)
{
std::vector<int> aVec, bVec;
std::string str;
while (std::getline(input, str))
{
std::stringstream ss{str};
int a, b;
ss >> a >> b;
aVec.emplace_back(a);
bVec.emplace_back(b);
}
return {aVec, bVec};
}
OUTPUT1(input)
{
std::vector<int> copyA = input.first;
std::vector<int> copyB = input.second;
std::sort(copyA.begin(), copyA.end());
std::sort(copyB.begin(), copyB.end());
int sum = 0;
for (int i = 0; i < input.first.size(); i++)
{
sum += std::abs(copyA[i] - copyB[i]);
}
return sum;
}
OUTPUT2(input)
{
std::map<int, int> idCount;
for (int i : input.second)
{
idCount[i]++;
}
int sum = 0;
for (int i : input.first)
{
sum += i * idCount[i];
}
return sum;
}
}
+75
View File
@@ -0,0 +1,75 @@
#include "common/aoc.h"
namespace day02
{
REGISTER_DAY(2024, Day02, std::vector<std::vector<int>>, int);
REGISTER_TEST_EXAMPLE(2024, Day02, ExampleInput, 1, 2);
REGISTER_TEST(2024, Day02, Input, 1, 549);
REGISTER_TEST_EXAMPLE(2024, Day02, ExampleInput, 2, 4);
REGISTER_TEST(2024, Day02, Input, 2, 589);
READ_INPUT(input)
{
std::vector<std::vector<int>> list;
std::string str;
while (std::getline(input, str))
{
std::stringstream ss{str};
list.emplace_back(Input::ReadInts(ss, ' '));
}
return list;
}
bool Validate(const std::vector<int>& vec)
{
bool lt = vec[0] < vec[1];
for (int j = 1; j < vec.size(); j++)
{
int diff = std::abs(vec[j] - vec[j - 1]);
if (vec[j - 1] < vec[j] != lt || diff < 1 || diff > 3)
{
return false;
}
}
return true;
}
bool ValidateWithout(const std::vector<int>& vec, int index)
{
std::vector<int> cpy = vec;
if (index < vec.size())
cpy.erase(cpy.begin() + index);
return Validate(cpy);
}
OUTPUT1(input)
{
int sum = 0;
for (int i = 0; i < input.size(); i++)
{
if (Validate(input[i]))
{
sum++;
}
}
return sum;
}
OUTPUT2(input)
{
int sum = 0;
for (int i = 0; i < input.size(); i++)
{
for (int j = 0; j < input[i].size() + 1; j++)
{
if (ValidateWithout(input[i], j))
{
sum++;
break;
}
}
}
return sum;
}
}
+50
View File
@@ -0,0 +1,50 @@
#include "common/aoc.h"
namespace day03
{
REGISTER_DAY(2024, Day03, std::string, int32_t);
REGISTER_TEST_EXAMPLE(2024, Day03, ExampleInput, 1, 161);
REGISTER_TEST(2024, Day03, Input, 1, 178794710);
REGISTER_TEST_EXAMPLE(2024, Day03, ExampleInput, 2, 48);
REGISTER_TEST(2024, Day03, Input, 2, 76729637);
READ_INPUT(input)
{
return Input::Read(input);
}
int32_t Mult(const std::string& str)
{
int i, j;
std::stringstream ss{str};
ss >> "mul(" >> i >> "," >> j >> ")";
return i * j;
}
OUTPUT1(input)
{
int32_t sum = 0;
std::vector<std::string> matches = Helper::GetAllRegexMatches(input, "mul\\([0-9]*,[0-9]*\\)");
for (auto& str : matches) sum += Mult(str);
return sum;
}
OUTPUT2(input)
{
int32_t sum = 0;
bool mul = true;
std::vector<std::string> matches =
Helper::GetAllRegexMatches(input, "(mul\\([0-9]*,[0-9]*\\)|do\\(\\)|don\'t\\(\\))");
for (auto& str : matches)
{
if (str == "do()")
mul = true;
else if (str == "don\'t()")
mul = false;
else if (mul)
sum += Mult(str);
}
return sum;
}
}
+76
View File
@@ -0,0 +1,76 @@
#include "common/aoc.h"
namespace day04
{
REGISTER_DAY(2024, Day04, Array2D<char>, int32_t);
REGISTER_TEST_EXAMPLE(2024, Day04, ExampleInput, 1, 18);
REGISTER_TEST(2024, Day04, Input, 1, 2543);
REGISTER_TEST_EXAMPLE(2024, Day04, ExampleInput, 2, 9);
REGISTER_TEST(2024, Day04, Input, 2, 1930);
READ_INPUT(input)
{
return Input::ReadArray2D(input);
}
OUTPUT1(input)
{
int32_t sum = 0;
for (int y = 0; y < input.height; y++)
{
for (int x = 0; x < input.width; x++)
{
if (y < input.height - 3 && input.Get(x, y) == 'X' && input.Get(x, y + 1) == 'M' &&
input.Get(x, y + 2) == 'A' && input.Get(x, y + 3) == 'S')
sum++;
if (y < input.height - 3 && input.Get(x, y) == 'S' && input.Get(x, y + 1) == 'A' &&
input.Get(x, y + 2) == 'M' && input.Get(x, y + 3) == 'X')
sum++;
if (x < input.width - 3 && input.Get(x, y) == 'X' && input.Get(x + 1, y) == 'M' && input.Get(x + 2, y) == 'A' &&
input.Get(x + 3, y) == 'S')
sum++;
if (x < input.width - 3 && input.Get(x, y) == 'S' && input.Get(x + 1, y) == 'A' && input.Get(x + 2, y) == 'M' &&
input.Get(x + 3, y) == 'X')
sum++;
if (y < input.height - 3 && x < input.width - 3 && input.Get(x, y) == 'X' && input.Get(x + 1, y + 1) == 'M' &&
input.Get(x + 2, y + 2) == 'A' && input.Get(x + 3, y + 3) == 'S')
sum++;
if (y < input.height - 3 && x < input.width - 3 && input.Get(x, y) == 'S' && input.Get(x + 1, y + 1) == 'A' &&
input.Get(x + 2, y + 2) == 'M' && input.Get(x + 3, y + 3) == 'X')
sum++;
if (y < input.height - 3 && x < input.width - 3 && input.Get(x + 3, y) == 'X' &&
input.Get(x + 2, y + 1) == 'M' && input.Get(x + 1, y + 2) == 'A' && input.Get(x, y + 3) == 'S')
sum++;
if (y < input.height - 3 && x < input.width - 3 && input.Get(x + 3, y) == 'S' &&
input.Get(x + 2, y + 1) == 'A' && input.Get(x + 1, y + 2) == 'M' && input.Get(x, y + 3) == 'X')
sum++;
}
}
return sum;
}
OUTPUT2(input)
{
int32_t sum = 0;
for (int y = 0; y < input.height - 2; y++)
{
for (int x = 0; x < input.width - 2; x++)
{
if (input.Get(x, y) == 'M' && input.Get(x, y + 2) == 'M' && input.Get(x + 1, y + 1) == 'A' &&
input.Get(x + 2, y) == 'S' && input.Get(x + 2, y + 2) == 'S')
sum++;
if (input.Get(x, y) == 'M' && input.Get(x, y + 2) == 'S' && input.Get(x + 1, y + 1) == 'A' &&
input.Get(x + 2, y) == 'M' && input.Get(x + 2, y + 2) == 'S')
sum++;
if (input.Get(x, y) == 'S' && input.Get(x, y + 2) == 'M' && input.Get(x + 1, y + 1) == 'A' &&
input.Get(x + 2, y) == 'S' && input.Get(x + 2, y + 2) == 'M')
sum++;
if (input.Get(x, y) == 'S' && input.Get(x, y + 2) == 'S' && input.Get(x + 1, y + 1) == 'A' &&
input.Get(x + 2, y) == 'M' && input.Get(x + 2, y + 2) == 'M')
sum++;
}
}
return sum;
}
}
+94
View File
@@ -0,0 +1,94 @@
#include "common/aoc.h"
namespace day05
{
struct Manual
{
std::vector<std::pair<int, int>> pairs;
std::vector<std::vector<int>> pages;
};
REGISTER_DAY(2024, Day05, Manual, int32_t);
REGISTER_TEST_EXAMPLE(2024, Day05, ExampleInput, 1, 143);
REGISTER_TEST(2024, Day05, Input, 1, 5964);
REGISTER_TEST_EXAMPLE(2024, Day05, ExampleInput, 2, 123);
REGISTER_TEST(2024, Day05, Input, 2, 4719);
READ_INPUT(input)
{
std::vector<std::pair<int, int>> pairs;
std::string str;
while (std::getline(input, str))
{
if (str.empty())
break;
int i, j;
std::stringstream ss{str};
ss >> i >> "|" >> j;
pairs.emplace_back(i, j);
}
std::vector<std::vector<int>> pages;
while (std::getline(input, str))
{
std::stringstream ss{str};
pages.emplace_back(Input::ReadInts(ss, ','));
}
return Manual{pairs, pages};
}
std::pair<bool, int> IsValid(const std::vector<int>& input, const std::vector<std::pair<int, int>>& pairs)
{
for (int i = 0; i < input.size() - 1; i++)
{
for (const auto& pair : pairs)
{
if (input[i] == pair.second && input[i + 1] == pair.first)
{
return {false, i};
}
}
}
return {true, 0};
}
std::vector<int> Sort(std::vector<int> input, const std::vector<std::pair<int, int>>& pairs)
{
bool valid;
do
{
auto [res, index] = IsValid(input, pairs);
if (!res)
std::swap(input[index], input[index + 1]);
valid = res;
} while (!valid);
return input;
}
OUTPUT1(input)
{
int32_t sum = 0;
for (const auto& man : input.pages)
{
if (IsValid(man, input.pairs).first)
sum += man[man.size() / 2];
}
return sum;
}
OUTPUT2(input)
{
int32_t sum = 0;
for (const auto& man : input.pages)
{
if (!IsValid(man, input.pairs).first)
{
auto newMan = Sort(man, input.pairs);
sum += newMan[newMan.size() / 2];
}
}
return sum;
}
}
+90
View File
@@ -0,0 +1,90 @@
#include "common/aoc.h"
namespace day06
{
REGISTER_DAY(2024, Day06, Array2D<char>, int32_t);
REGISTER_TEST_EXAMPLE(2024, Day06, ExampleInput, 1, 41);
REGISTER_TEST(2024, Day06, Input, 1, 4776);
REGISTER_TEST_EXAMPLE(2024, Day06, ExampleInput, 2, 6);
REGISTER_TEST(2024, Day06, Input, 2, 1586);
READ_INPUT(input)
{
return Input::ReadArray2D(input);
}
bool TestObstacle(const Array2D<char>& map, Index2D obstacle, Index2D startPos, Index2D startDir)
{
std::set<std::pair<Index2D, Index2D>> visited;
Index2D index = startPos;
Index2D dir = startDir;
while (map.IsInside(index) && visited.find(std::pair{index, dir}) == visited.end())
{
visited.emplace(index, dir);
Index2D newIndex{index.x + dir.x, index.y + dir.y};
if ((map.IsInside(newIndex) && map.Get(newIndex) == '#') || newIndex == obstacle)
{
Index2D newDir{-dir.y, dir.x};
dir = newDir;
}
else
{
index = newIndex;
}
}
return visited.find(std::pair{index, dir}) != visited.end();
}
OUTPUT1(input)
{
std::set<Index2D> visited;
Index2D index = input.Find('^');
Index2D dir{0, -1};
while (input.IsInside(index))
{
visited.emplace(index);
Index2D newIndex{index.x + dir.x, index.y + dir.y};
if (input.IsInside(newIndex) && input.Get(newIndex) == '#')
{
Index2D newDir{-dir.y, dir.x};
dir = newDir;
}
else
{
index = newIndex;
}
}
return visited.size();
}
OUTPUT2(input)
{
std::set<Index2D> visited;
Index2D index = input.Find('^');
Index2D dir{0, -1};
int32_t sum = 0;
while (input.IsInside(index))
{
Index2D newIndex{index.x + dir.x, index.y + dir.y};
if (input.IsInside(newIndex) && input.Get(newIndex) == '#')
{
Index2D newDir{-dir.y, dir.x};
dir = newDir;
}
else
{
if (visited.count(newIndex) == 0)
{
if (TestObstacle(input, newIndex, index, dir))
sum++;
}
visited.emplace(index);
index = newIndex;
}
}
return sum;
}
}
+94
View File
@@ -0,0 +1,94 @@
#include "common/aoc.h"
namespace day07
{
struct Data
{
int64_t result;
std::vector<int64_t> terms;
};
REGISTER_DAY(2024, Day07, std::vector<Data>, int64_t);
REGISTER_TEST_EXAMPLE(2024, Day07, ExampleInput, 1, 3749);
REGISTER_TEST(2024, Day07, Input, 1, 945512582195);
REGISTER_TEST_EXAMPLE(2024, Day07, ExampleInput, 2, 11387);
REGISTER_TEST(2024, Day07, Input, 2, 271691107779347);
READ_INPUT(input)
{
std::vector<Data> datas;
std::string str;
while (std::getline(input, str))
{
std::stringstream ss{str};
Data data;
ss >> data.result >> ": ";
while (std::getline(ss, str, ' '))
{
data.terms.emplace_back(std::stoll(str));
}
datas.emplace_back(data);
}
return datas;
}
bool CanCalculateResultP1(const Data& data, int index, int64_t current)
{
if (index >= data.terms.size())
return data.result == current;
if (index == 0)
return CanCalculateResultP1(data, index + 1, data.terms[index]);
if (CanCalculateResultP1(data, index + 1, current + data.terms[index]))
return true;
else if (CanCalculateResultP1(data, index + 1, current * data.terms[index]))
return true;
return false;
}
int64_t Concat(int64_t i1, int64_t i2)
{
int64_t base = 10;
while (i2 >= base) base *= 10;
return i1 * base + i2;
}
bool CanCalculateResultP2(const Data& data, int index, int64_t current)
{
if (index >= data.terms.size())
return data.result == current;
if (index == 0)
return CanCalculateResultP2(data, index + 1, data.terms[index]);
if (CanCalculateResultP2(data, index + 1, current + data.terms[index]))
return true;
else if (CanCalculateResultP2(data, index + 1, current * data.terms[index]))
return true;
else if (CanCalculateResultP2(data, index + 1, Concat(current, data.terms[index])))
return true;
return false;
}
OUTPUT1(input)
{
int64_t sum = 0;
for (const auto& data : input)
{
if (CanCalculateResultP1(data, 0, 0))
sum += data.result;
}
return sum;
}
OUTPUT2(input)
{
int64_t sum = 0;
for (const auto& data : input)
{
if (CanCalculateResultP2(data, 0, 0))
sum += data.result;
}
return sum;
}
}
+94
View File
@@ -0,0 +1,94 @@
#include "common/aoc.h"
namespace day08
{
struct Data
{
std::map<char, std::vector<Index2D>> map;
int width;
int height;
};
REGISTER_DAY(2024, Day08, Data, int32_t);
REGISTER_TEST_EXAMPLE(2024, Day08, ExampleInput, 1, 14);
REGISTER_TEST(2024, Day08, Input, 1, 244);
REGISTER_TEST_EXAMPLE(2024, Day08, ExampleInput, 2, 34);
REGISTER_TEST(2024, Day08, Input, 2, 912);
READ_INPUT(input)
{
Data data;
std::string str;
int y = 0;
while (std::getline(input, str))
{
for (int i = 0; i < str.size(); i++)
{
if (str[i] != '.')
data.map[str[i]].emplace_back(Index2D{i, y});
}
data.width = str.size();
y++;
}
data.height = y;
return data;
}
OUTPUT1(input)
{
std::set<Index2D> antinodes;
int32_t sum = 0;
for (auto& [freq, locations] : input.map)
{
for (int i = 0; i < locations.size(); i++)
{
for (int j = i + 1; j < locations.size(); j++)
{
Index2D diff = locations[i] - locations[j];
Index2D newPos1 = locations[i] + diff;
Index2D newPos2 = locations[j] - diff;
if (newPos1.x >= 0 && newPos1.x < input.width && newPos1.y >= 0 && newPos1.y < input.height)
antinodes.emplace(newPos1);
if (newPos2.x >= 0 && newPos2.x < input.width && newPos2.y >= 0 && newPos2.y < input.height)
antinodes.emplace(newPos2);
}
}
}
return antinodes.size();
}
OUTPUT2(input)
{
std::set<Index2D> antinodes;
for (auto& [freq, locations] : input.map)
{
for (int i = 0; i < locations.size(); i++)
{
for (int j = i + 1; j < locations.size(); j++)
{
Index2D diff = locations[i] - locations[j];
int offset = 0;
while (true)
{
Index2D newPos = locations[i] + diff * offset;
if (newPos.x < 0 || newPos.x >= input.width || newPos.y < 0 || newPos.y >= input.height)
break;
antinodes.emplace(newPos);
offset++;
}
offset = 0;
while (true)
{
Index2D newPos = locations[j] - diff * offset;
if (newPos.x < 0 || newPos.x >= input.width || newPos.y < 0 || newPos.y >= input.height)
break;
antinodes.emplace(newPos);
offset++;
}
}
}
}
return antinodes.size();
}
}
+143
View File
@@ -0,0 +1,143 @@
#include "common/aoc.h"
namespace day09
{
REGISTER_DAY(2024, Day09, std::vector<int>, int64_t);
REGISTER_TEST_EXAMPLE(2024, Day09, ExampleInput, 1, 1928);
REGISTER_TEST(2024, Day09, Input, 1, 6283404590840);
REGISTER_TEST_EXAMPLE(2024, Day09, ExampleInput, 2, 2858);
REGISTER_TEST(2024, Day09, Input, 2, 6304576012713);
READ_INPUT(input)
{
std::string str;
std::getline(input, str);
if (str.size() % 2 == 0)
str.pop_back();
std::vector<int> disk;
std::transform(str.begin(), str.end(), std::back_inserter(disk), [](char c) { return c - '0'; });
return disk;
}
struct File
{
int64_t pos;
int64_t size;
int64_t id;
};
OUTPUT1(input)
{
int64_t sum = 0;
std::vector<std::pair<int, int>> resMemory;
resMemory.emplace_back(input[0], 0);
int currentFreeIndex = 1;
int currentFreeMemory = input[currentFreeIndex];
int currentMemoryIndex = input.size() - 1;
int currentMemoryToMove = input[currentMemoryIndex];
while (true)
{
if (currentMemoryToMove <= currentFreeMemory)
{
resMemory.emplace_back(currentMemoryToMove, currentMemoryIndex / 2);
currentFreeMemory -= currentMemoryToMove;
currentMemoryIndex -= 2;
currentMemoryToMove = input[currentMemoryIndex];
if (currentMemoryIndex < currentFreeIndex)
break;
}
else
{
resMemory.emplace_back(currentFreeMemory, currentMemoryIndex / 2);
currentMemoryToMove -= currentFreeMemory;
currentFreeMemory = 0;
}
if (currentFreeMemory == 0)
{
if (currentFreeIndex + 1 == currentMemoryIndex)
{
resMemory.back().first += currentMemoryToMove;
break;
}
else
{
resMemory.emplace_back(input[currentFreeIndex + 1], (currentFreeIndex + 1) / 2);
currentFreeIndex += 2;
currentFreeMemory = input[currentFreeIndex];
}
}
}
int64_t pos = 0;
for (auto [count, id] : resMemory)
{
// Based on sum of integer formula:
// S = numberOfIntegers * (firstNumber + lastNumber) / 2
int64_t firstNumber = pos;
int64_t lastNumber = pos + count - 1;
sum += count * (firstNumber + lastNumber) / 2 * id;
pos += count;
}
return sum;
}
OUTPUT2(input)
{
int64_t sum = 0;
std::map<int64_t, int64_t> idToPos;
std::vector<File> resMemory;
std::vector<std::pair<int64_t, int64_t>> freeMemory;
int pos = 0;
for (int i = 0; i < input.size(); i += 2)
{
idToPos.emplace(i / 2, pos);
pos += input[i];
if (i != input.size() - 1)
{
freeMemory.emplace_back(pos, input[i + 1]);
pos += input[i + 1];
}
}
for (int i = input.size() - 1; i >= 0; i -= 2)
{
int fileSize = input[i];
int fileId = i / 2;
int filePos = idToPos[fileId];
for (int j = 0; j < freeMemory.size(); j++)
{
auto& [freeMemoryPos, freeMemorySize] = freeMemory[j];
if (freeMemoryPos > filePos)
{
// Couldn't find a suitable place for the file, don't move it
resMemory.emplace_back(File{filePos, fileSize, fileId});
break;
}
if (freeMemorySize >= fileSize)
{
resMemory.emplace_back(File{freeMemoryPos, fileSize, fileId});
freeMemorySize -= fileSize;
freeMemoryPos += fileSize;
if (freeMemorySize == 0)
freeMemory.erase(freeMemory.begin() + j);
break;
}
}
}
for (const auto& file : resMemory)
{
// Based on sum of integer formula:
// S = numberOfIntegers * (firstNumber + lastNumber) / 2
int64_t firstNumber = file.pos;
int64_t lastNumber = file.pos + file.size - 1;
sum += file.size * (firstNumber + lastNumber) / 2 * file.id;
}
return sum;
}
}
+84
View File
@@ -0,0 +1,84 @@
#include "common/aoc.h"
namespace day10
{
REGISTER_DAY(2024, Day10, Array2D<int>, int32_t);
REGISTER_TEST_EXAMPLE(2024, Day10, ExampleInput, 1, 36);
REGISTER_TEST(2024, Day10, Input, 1, 786);
REGISTER_TEST_EXAMPLE(2024, Day10, ExampleInput, 2, 81);
REGISTER_TEST(2024, Day10, Input, 2, 1722);
READ_INPUT(input)
{
return Input::ReadDigitsAsArray2D(input);
}
void GetTops(const Array2D<int>& map, Index2D pos, std::set<Index2D>& visited)
{
int val = map.Get(pos);
if (val == 9)
{
visited.emplace(pos);
return;
}
std::vector<Index2D> indices{Index2D{-1, 0}, Index2D{1, 0}, Index2D{0, -1}, Index2D{0, 1}};
for (auto index : indices)
{
if (map.IsInside(pos + index) && map.Get(pos + index) == val + 1)
GetTops(map, pos + index, visited);
}
}
int CountPaths(const Array2D<int>& map, Index2D pos)
{
int val = map.Get(pos);
if (val == 9)
return 1;
int paths = 0;
std::vector<Index2D> indices{Index2D{-1, 0}, Index2D{1, 0}, Index2D{0, -1}, Index2D{0, 1}};
for (auto index : indices)
{
if (map.IsInside(pos + index) && map.Get(pos + index) == val + 1)
paths += CountPaths(map, pos + index);
}
return paths;
}
OUTPUT1(input)
{
int32_t sum = 0;
for (int y = 0; y < input.height; y++)
{
for (int x = 0; x < input.width; x++)
{
if (input.Get(x, y) == 0)
{
std::set<Index2D> visited;
GetTops(input, Index2D{x, y}, visited);
sum += visited.size();
}
}
}
return sum;
}
OUTPUT2(input)
{
int32_t sum = 0;
for (int y = 0; y < input.height; y++)
{
for (int x = 0; x < input.width; x++)
{
if (input.Get(x, y) == 0)
{
sum += CountPaths(input, Index2D{x, y});
}
}
}
return sum;
}
}
+96
View File
@@ -0,0 +1,96 @@
#include "common/aoc.h"
namespace day11
{
using Stones = std::map<int64_t, int64_t>;
REGISTER_DAY(2024, Day11, Stones, int64_t);
REGISTER_TEST_EXAMPLE(2024, Day11, ExampleInput, 1, 55312);
REGISTER_TEST(2024, Day11, Input, 1, 189167);
REGISTER_TEST_EXAMPLE(2024, Day11, ExampleInput, 2, 65601038650482);
REGISTER_TEST(2024, Day11, Input, 2, 225253278506288);
READ_INPUT(input)
{
Stones stones;
for (auto i : Input::ReadInt64s(input, ' ')) stones[i]++;
return stones;
}
int logi10(int64_t i)
{
int log = 1;
int64_t base = 10;
while (i >= base)
{
base *= 10;
log++;
}
return log;
}
int64_t pow10(int i)
{
int64_t pow = 1;
for (int j = 0; j < i; j++) pow *= 10;
return pow;
}
std::pair<int64_t, int64_t> split(int64_t i)
{
int log = logi10(i);
int64_t pow = pow10(log / 2);
return {i / pow, i % pow};
}
void Step(std::map<int64_t, int64_t>& stones)
{
std::map<int64_t, int64_t> next;
for (auto [cur, count] : stones)
{
if (cur == 0)
{
next[1] += count;
}
else if (logi10(cur) % 2 == 0)
{
auto [i1, i2] = split(cur);
next[i1] += count;
next[i2] += count;
}
else
{
next[cur * 2024] += count;
}
}
stones = std::move(next);
}
int64_t CalculateStones(const std::map<int64_t, int64_t>& stones, int iterations)
{
std::map<int64_t, int64_t> cpy = stones;
for (int i = 0; i < iterations; i++) Step(cpy);
int64_t sum = 0;
for (auto& [cur, count] : cpy)
{
sum += count;
}
return sum;
}
OUTPUT1(input)
{
return CalculateStones(input, 25);
}
OUTPUT2(input)
{
return CalculateStones(input, 75);
}
}
+117
View File
@@ -0,0 +1,117 @@
#include "common/aoc.h"
namespace day12
{
REGISTER_DAY(2024, Day12, Array2D<char>, int);
REGISTER_TEST_EXAMPLE(2024, Day12, ExampleInput, 1, 1930);
REGISTER_TEST(2024, Day12, Input, 1, 1550156);
REGISTER_TEST_EXAMPLE(2024, Day12, ExampleInput, 2, 1206);
REGISTER_TEST(2024, Day12, Input, 2, 946084);
READ_INPUT(input)
{
return Input::ReadArray2D(input);
}
void CalculateAreaAndPerimiter(const Array2D<char>& map,
Index2D position,
std::set<Index2D>& totalVisited,
std::set<Index2D>& area,
std::set<std::pair<Index2D, Index2D>>& perimeter)
{
char c = map.Get(position);
std::stack<Index2D> toEvaluate;
toEvaluate.emplace(position);
area.emplace(position);
while (!toEvaluate.empty())
{
Index2D current = toEvaluate.top();
toEvaluate.pop();
totalVisited.emplace(current);
for (auto& dir : Helper::GetNeighborDirections())
{
Index2D newPos = current + dir;
if (area.count(newPos) == 0)
{
if (map.IsInside(newPos) && map.Get(newPos) == c)
{
toEvaluate.emplace(newPos);
area.emplace(newPos);
}
else
perimeter.emplace(current, dir);
}
}
}
}
void RemovePerimetersNextToPos(std::set<std::pair<Index2D, Index2D>>& perimeter,
Index2D pos,
Index2D normal,
Index2D dir)
{
Index2D next = pos + dir;
auto it = perimeter.find({next, normal});
while (it != perimeter.end())
{
perimeter.erase(it);
next = next + dir;
it = perimeter.find({next, normal});
}
}
int CalculatePerimeterSideSize(std::set<std::pair<Index2D, Index2D>>& perimeter)
{
int perimeterSize = 0;
while (!perimeter.empty())
{
auto [index, dir] = *perimeter.begin();
perimeter.erase(perimeter.begin());
perimeterSize++;
RemovePerimetersNextToPos(perimeter, index, dir, Index2D{dir.y, dir.x});
RemovePerimetersNextToPos(perimeter, index, dir, Index2D{-dir.y, -dir.x});
}
return perimeterSize;
}
OUTPUT1(input)
{
int sum = 0;
std::set<Index2D> visited;
for (auto it = input.begin(); it != input.end(); it++)
{
Index2D index = it.index;
if (visited.count(index) == 0)
{
std::set<Index2D> area;
std::set<std::pair<Index2D, Index2D>> perimeter;
CalculateAreaAndPerimiter(input, it.index, visited, area, perimeter);
sum += perimeter.size() * area.size();
}
}
return sum;
}
OUTPUT2(input)
{
int sum = 0;
std::set<Index2D> visited;
for (auto it = input.begin(); it != input.end(); it++)
{
if (visited.count(it.index) == 0)
{
std::set<Index2D> area;
std::set<std::pair<Index2D, Index2D>> perimeter;
CalculateAreaAndPerimiter(input, it.index, visited, area, perimeter);
int perimeterSize = CalculatePerimeterSideSize(perimeter);
sum += perimeterSize * area.size();
}
}
return sum;
}
}
+102
View File
@@ -0,0 +1,102 @@
#include "common/aoc.h"
namespace day13
{
struct ClawMachine
{
int64_t buttonAX;
int64_t buttonAY;
int64_t buttonBX;
int64_t buttonBY;
int64_t prizeX;
int64_t prizeY;
};
REGISTER_DAY(2024, Day13, std::vector<ClawMachine>, int64_t);
REGISTER_TEST_EXAMPLE(2024, Day13, ExampleInput, 1, 480);
REGISTER_TEST(2024, Day13, Input, 1, 26005);
REGISTER_TEST_EXAMPLE(2024, Day13, ExampleInput, 2, 875318608908);
REGISTER_TEST(2024, Day13, Input, 2, 105620095782547);
READ_INPUT(input)
{
std::vector<ClawMachine> clawMachines;
std::string str;
while (std::getline(input, str))
{
ClawMachine machine;
std::stringstream ss{str};
ss >> "Button A: X+" >> machine.buttonAX >> ", Y+" >> machine.buttonAY;
std::getline(input, str);
ss = std::stringstream{str};
ss >> "Button B: X+" >> machine.buttonBX >> ", Y+" >> machine.buttonBY;
std::getline(input, str);
ss = std::stringstream{str};
ss >> "Prize: X=" >> machine.prizeX >> ", Y=" >> machine.prizeY;
std::getline(input, str);
clawMachines.emplace_back(machine);
}
return clawMachines;
}
int64_t GetCost(const ClawMachine& machine)
{
// Solve
// aX * a + bX * b = pX
// aY * a + bY * b = pY
// aY * aX * a + aY * bX * b = aY * pX
// -aY * aX * a + -aX * bY * b = -aX * pY
// (aY * bX * b) + (-aX * bY * b) = aY * pX + (-aX * pY)
// (aY * bX - aX * bY) * b = aY * pX - aX * pY
// b = (aY * pX - aX * pY) / (aY * bX - aX * bY)
// a = (pX - bX * b) / aX
int64_t aYbX = machine.buttonAY * machine.buttonBX;
int64_t aYpX = machine.buttonAY * machine.prizeX;
int64_t aXbY = machine.buttonAX * machine.buttonBY;
int64_t aXpY = machine.buttonAX * machine.prizeY;
if (aYbX - aXbY == 0)
{
std::cerr << "Multiple solutions" << std::endl;
}
else if ((aYpX - aXpY) % (aYbX - aXbY) == 0) // integer solution exists
{
int64_t b = (aYpX - aXpY) / (aYbX - aXbY);
int64_t a = (machine.prizeX - b * machine.buttonBX) / machine.buttonAX;
if (a >= 0 && b >= 0)
return b + a * 3;
}
return 0; // No solution
}
OUTPUT1(input)
{
int64_t sum = 0;
for (auto& machine : input)
{
sum += GetCost(machine);
}
return sum;
}
OUTPUT2(input)
{
int64_t sum = 0;
for (auto machine : input)
{
machine.prizeX += 10000000000000;
machine.prizeY += 10000000000000;
sum += GetCost(machine);
}
return sum;
}
}
+98
View File
@@ -0,0 +1,98 @@
#include "common/aoc.h"
namespace day14
{
struct Robot
{
Index2D pos;
Index2D vel;
};
REGISTER_DAY(2024, Day14, std::vector<Robot>, int32_t);
REGISTER_TEST_EXAMPLE(2024, Day14, ExampleInput, 1, 12);
REGISTER_TEST(2024, Day14, Input, 1, 228421332);
// REGISTER_TEST_EXAMPLE(2024, Day14, ExampleInput, 2, 31);
REGISTER_TEST(2024, Day14, Input, 2, 7790);
READ_INPUT(input)
{
std::vector<Robot> robots;
std::string str;
while (std::getline(input, str))
{
Robot robot;
std::stringstream ss{str};
ss >> "p=" >> robot.pos.x >> "," >> robot.pos.y >> "v=" >> robot.vel.x >> "," >> robot.vel.y;
robots.emplace_back(robot);
}
return robots;
}
OUTPUT1(input)
{
int width = 101;
int height = 103;
if (isExample)
{
width = 11;
height = 7;
}
int a{0}, b{0}, c{0}, d{0};
for (auto& robot : input)
{
Index2D endPos = robot.pos + robot.vel * 100;
endPos.x = (endPos.x % width + width) % width;
endPos.y = (endPos.y % height + height) % height;
if (endPos.x < width / 2 && endPos.y < height / 2)
a++;
if (endPos.x < width / 2 && endPos.y > height / 2)
b++;
if (endPos.x > width / 2 && endPos.y < height / 2)
c++;
if (endPos.x > width / 2 && endPos.y > height / 2)
d++;
}
return a * b * c * d;
}
int GetStepCoord(const std::vector<Robot>& robots, int length, int dimension)
{
int maxIndex = 0;
int maxNeighbors = 0;
for (int i = 1; i <= length; i++)
{
std::vector<int> amounts(length, 0);
for (const auto& robot : robots)
{
int endPos = robot.pos[dimension] + robot.vel[dimension] * i;
endPos = (endPos % length + length) % length;
amounts[endPos]++;
}
int neighbors = 0;
for (int i = 0; i < length; i++)
{
neighbors += amounts[i] * (amounts[(i + 1) % length] + amounts[(i + length - 1) % length]);
}
if (neighbors > maxNeighbors)
{
maxNeighbors = neighbors;
maxIndex = i;
}
}
return maxIndex;
}
OUTPUT2(input)
{
int width = 101;
int height = 103;
int stepX = GetStepCoord(input, width, 0);
int stepY = GetStepCoord(input, height, 1);
return Helper::ChineseRemainderTheorem({width, height}, {stepX, stepY});
}
}
+198
View File
@@ -0,0 +1,198 @@
#include "common/aoc.h"
namespace day15
{
struct Map
{
Array2D<char> map;
std::string input;
};
REGISTER_DAY(2024, Day15, Map, int64_t);
REGISTER_TEST_EXAMPLE(2024, Day15, ExampleInput, 1, 10092);
REGISTER_TEST(2024, Day15, Input, 1, 1526673);
REGISTER_TEST_EXAMPLE(2024, Day15, ExampleInput, 2, 9021);
REGISTER_TEST(2024, Day15, Input, 2, 1535509);
READ_INPUT(input)
{
Map map;
map.map = Input::ReadArray2D(input);
std::string str;
while (std::getline(input, str)) map.input += str;
return map;
}
void Move(Array2D<char>& map, Index2D boxPos, Index2D dir)
{
if (dir.x == 0)
{
char leftC = map.Get(boxPos.x, boxPos.y + dir.y);
if (leftC == '[')
Move(map, Index2D{boxPos.x, boxPos.y + dir.y}, dir);
if (leftC == ']')
Move(map, Index2D{boxPos.x - 1, boxPos.y + dir.y}, dir);
char rightC = map.Get(boxPos.x + 1, boxPos.y + dir.y);
if (rightC == '[')
Move(map, Index2D{boxPos.x + 1, boxPos.y + dir.y}, dir);
if (rightC == ']')
Move(map, Index2D{boxPos.x, boxPos.y + dir.y}, dir);
map.Set(boxPos.x, boxPos.y + dir.y, '[');
map.Set(boxPos.x + 1, boxPos.y + dir.y, ']');
map.Set(boxPos.x, boxPos.y, '.');
map.Set(boxPos.x + 1, boxPos.y, '.');
}
else
{
if (map.Get(boxPos.x + dir.x * 2, boxPos.y) == '[')
Move(map, Index2D{boxPos.x + dir.x * 2, boxPos.y}, dir);
map.Set(boxPos.x + dir.x, boxPos.y, '[');
map.Set(boxPos.x + dir.x + 1, boxPos.y, ']');
}
}
bool CanMove(const Array2D<char>& map, Index2D boxPos, Index2D dir);
bool CanMoveSide(const Array2D<char>& map, Index2D boxSidePos, Index2D dir)
{
char c = map[boxSidePos];
if (c == '.')
return true;
else if (c == '[')
return CanMove(map, Index2D{boxSidePos.x, boxSidePos.y}, dir);
else if (c == ']')
return CanMove(map, Index2D{boxSidePos.x - 1, boxSidePos.y}, dir);
return false;
}
// boxPos is position of [
bool CanMove(const Array2D<char>& map, Index2D boxPos, Index2D dir)
{
if (dir.x == 0)
{
bool canMoveLeftSide = CanMoveSide(map, Index2D{boxPos.x, boxPos.y + dir.y}, dir);
bool canMoveRightSide = CanMoveSide(map, Index2D{boxPos.x + 1, boxPos.y + dir.y}, dir);
return canMoveLeftSide && canMoveRightSide;
}
else
{
Index2D pos = boxPos + dir;
while (true)
{
char n = map[pos];
if (n == '.')
return true;
else if (n == '#')
return false;
pos = pos + dir;
}
}
}
int64_t CalculateResult(const Array2D<char>& map)
{
int64_t sum = 0;
for (auto it = map.begin(); it != map.end(); it++)
if (*it == 'O' || *it == '[')
sum += it.index.x + it.index.y * 100;
return sum;
}
OUTPUT1(input)
{
Map cpy = input;
Index2D position = cpy.map.Find('@');
for (auto& dirC : cpy.input)
{
Index2D dir = Helper::GetDirection(dirC);
char nextChar = cpy.map[position + dir];
if (nextChar == '.')
{
cpy.map[position] = '.';
cpy.map[position + dir] = '@';
position = position + dir;
}
else if (nextChar == 'O')
{
Index2D pos = position + dir;
while (true)
{
char n = cpy.map[pos];
if (n == '.')
{
cpy.map[position] = '.';
cpy.map[position + dir] = '@';
cpy.map[pos] = 'O';
position = position + dir;
break;
}
else if (n == '#')
break;
pos = pos + dir;
}
}
}
return CalculateResult(cpy.map);
}
OUTPUT2(input)
{
Array2D<char> map{input.map.width * 2, input.map.height, '.'};
for (auto it = input.map.begin(); it != input.map.end(); it++)
{
if (*it == 'O')
{
map.Set(it.index.x * 2, it.index.y, '[');
map.Set(it.index.x * 2 + 1, it.index.y, ']');
}
else if (*it == '#')
{
map.Set(it.index.x * 2, it.index.y, '#');
map.Set(it.index.x * 2 + 1, it.index.y, '#');
}
else if (*it == '@')
{
map.Set(it.index.x * 2, it.index.y, '@');
}
}
Index2D position = map.Find('@');
for (auto& dirC : input.input)
{
Index2D dir = Helper::GetDirection(dirC);
char nextChar = map[position + dir];
if (nextChar == '.')
{
map[position] = '.';
map[position + dir] = '@';
position = position + dir;
}
else if (nextChar == ']' || nextChar == '[')
{
Index2D boxPos = position + dir;
if (nextChar == ']')
boxPos.x -= 1;
if (CanMove(map, boxPos, dir))
{
Move(map, boxPos, dir);
map[position] = '.';
map[position + dir] = '@';
position = position + dir;
}
}
}
return CalculateResult(map);
}
}
+55
View File
@@ -0,0 +1,55 @@
#include "common/aoc.h"
namespace day16
{
REGISTER_DAY(2024, Day16, Array2D<char>, int32_t);
REGISTER_TEST_EXAMPLE(2024, Day16, ExampleInput, 1, 7036);
REGISTER_TEST(2024, Day16, Input, 1, 72428);
REGISTER_TEST_EXAMPLE(2024, Day16, ExampleInput, 2, 45);
REGISTER_TEST(2024, Day16, Input, 2, 456);
READ_INPUT(input)
{
return Input::ReadArray2D(input);
}
std::vector<std::pair<int, std::pair<Index2D, Index2D>>> Branch(const Array2D<char>& map,
const std::pair<Index2D, Index2D>& state)
{
std::vector<std::pair<int, std::pair<Index2D, Index2D>>> paths;
if (map.Get(state.first + state.second) != '#')
paths.emplace_back(1, std::pair{state.first + state.second, state.second});
Index2D dir2 = Index2D{state.second.y, -state.second.x};
if (map.Get(state.first + dir2) != '#')
paths.emplace_back(1001, std::pair{state.first + dir2, dir2});
Index2D dir3 = Index2D{-state.second.y, state.second.x};
if (map.Get(state.first + dir3) != '#')
paths.emplace_back(1001, std::pair{state.first + dir3, dir3});
return paths;
}
bool Goal(const Array2D<char>& map, const std::pair<Index2D, Index2D>& state)
{
return map.Get(state.first) == 'E';
}
OUTPUT1(input)
{
return Helper::Dijkstras(input, std::pair{input.Find('S'), Index2D{1, 0}}, Branch, Goal);
}
OUTPUT2(input)
{
std::set<std::pair<Index2D, Index2D>> allVisited =
Helper::DijkstrasAllVisited(input, std::pair{input.Find('S'), Index2D{1, 0}}, Branch, Goal);
std::set<Index2D> solutions;
for (const auto& [pos, dir] : allVisited) solutions.emplace(pos);
return solutions.size();
}
}
+173
View File
@@ -0,0 +1,173 @@
#include <bitset>
#include "common/aoc.h"
namespace day17
{
struct Program
{
int64_t A;
int64_t B;
int64_t C;
std::vector<int> instructions;
};
REGISTER_DAY(2024, Day17, Program, int64_t);
REGISTER_TEST_EXAMPLE(2024, Day17, ExampleInput, 1, 0);
REGISTER_TEST(2024, Day17, Input, 1, 0);
REGISTER_TEST_EXAMPLE(2024, Day17, ExampleInput, 2, 117440);
REGISTER_TEST(2024, Day17, Input, 2, 202322348616234);
READ_INPUT(input)
{
Program program;
std::string str;
std::getline(input, str);
std::stringstream ss{str};
ss >> "Register A: " >> program.A;
std::getline(input, str);
ss = std::stringstream{str};
ss >> "Register B: " >> program.B;
std::getline(input, str);
ss = std::stringstream{str};
ss >> "Register C: " >> program.C;
std::getline(input, str);
std::getline(input, str, ' ');
program.instructions = Input::ReadInts(input, ',');
return program;
}
int64_t Combo(int i, int64_t a, int64_t b, int64_t c)
{
if (i <= 3)
return i;
if (i == 4)
return a;
if (i == 5)
return b;
if (i == 6)
return c;
std::cerr << "Invalid combo: " << i << std::endl;
return -1;
}
std::vector<int> RunProgram(const Program& program)
{
int64_t a = program.A;
int64_t b = program.B;
int64_t c = program.C;
int pc = 0;
std::vector<int> result;
while (pc < program.instructions.size())
{
int ins = program.instructions[pc];
int arg = program.instructions[pc + 1];
if (ins == 0)
a = a >> Combo(arg, a, b, c);
else if (ins == 1)
b = b ^ arg;
else if (ins == 2)
b = Combo(arg, a, b, c) % 8;
else if (ins == 3 && a != 0)
pc = arg - 2;
else if (ins == 4)
b = b ^ c;
else if (ins == 5)
result.emplace_back(Combo(arg, a, b, c) % 8);
else if (ins == 6)
b = a >> Combo(arg, a, b, c);
else if (ins == 7)
c = a >> Combo(arg, a, b, c);
pc += 2;
}
return result;
}
std::vector<int64_t> GetDiffPeriodicity(const std::vector<int64_t>& ints)
{
if (ints.empty())
return {};
const int MIN_LOOP_COUNT = 3;
if ((ints.size() - 1) % MIN_LOOP_COUNT != 0)
return {};
std::vector<int64_t> diffs;
for (int i = 0; i < ints.size() - 1; i++)
{
diffs.emplace_back(ints[i + 1] - ints[i]);
}
int loopSize = ints.size() / MIN_LOOP_COUNT;
for (int i = 0; i < MIN_LOOP_COUNT - 1; i++)
{
for (int j = 0; j < loopSize; j++)
{
if (diffs[i * loopSize + j] != diffs[i * loopSize + loopSize + j])
{
return {};
}
}
}
return {diffs.begin(), diffs.begin() + loopSize};
}
OUTPUT1(input)
{
std::vector<int> result = RunProgram(input);
std::cout << result << std::endl;
return 0;
}
OUTPUT2(input)
{
int64_t aRegister = 0;
int currentDepth = 0;
std::vector<int64_t> diffs{1};
std::vector<int64_t> aRegisterSolution{};
Program program = input;
int offset = 0;
while (true)
{
program.A = aRegister;
std::vector<int> vec = RunProgram(program);
if (vec == input.instructions)
{
return aRegister;
}
if (vec.size() > currentDepth && vec[currentDepth] == program.instructions[currentDepth])
{
aRegisterSolution.emplace_back(aRegister);
}
std::vector<int64_t> loop = GetDiffPeriodicity(aRegisterSolution);
if (!loop.empty())
{
aRegister = aRegisterSolution[0];
diffs = loop;
aRegisterSolution.clear();
offset = 0;
currentDepth++;
}
else
{
aRegister += diffs[offset % diffs.size()];
offset++;
}
}
return 0;
}
}
+96
View File
@@ -0,0 +1,96 @@
#include "common/aoc.h"
namespace day18
{
using Memory = std::map<Index2D, int>;
REGISTER_DAY(2024, Day18, Memory, int32_t);
REGISTER_TEST_EXAMPLE(2024, Day18, ExampleInput, 1, 22);
REGISTER_TEST(2024, Day18, Input, 1, 304);
REGISTER_TEST_EXAMPLE(2024, Day18, ExampleInput, 2, 20);
REGISTER_TEST(2024, Day18, Input, 2, 2876);
READ_INPUT(input)
{
std::map<Index2D, int> memory;
std::string str;
int i = 0;
while (std::getline(input, str))
{
Index2D index;
std::stringstream ss{str};
ss >> index.x >> "," >> index.y;
memory.emplace(index, i);
i++;
}
return memory;
}
struct Map
{
const std::map<Index2D, int>& memory;
int size;
int time;
};
std::vector<std::pair<int, Index2D>> Branch(const Map& map, const Index2D& current)
{
std::vector<std::pair<int, Index2D>> branch;
for (auto& dir : Helper::GetNeighborDirections())
{
Index2D pos = current + dir;
if (pos.x < 0 || pos.y < 0 || pos.x > map.size || pos.y > map.size)
continue;
auto it = map.memory.find(pos);
if (it == map.memory.end() || it->second >= map.time)
branch.emplace_back(1, pos);
}
return branch;
}
bool Goal(const Map& map, const Index2D& current)
{
return current.x == map.size && current.y == map.size;
}
bool ValidPath(const Map& map, int index)
{
Map cpy = map;
cpy.time = index;
return Helper::Dijkstras(cpy, Index2D{0, 0}, Branch, Goal) != 0;
}
OUTPUT1(input)
{
int32_t sum = 0;
int time = 1024;
int size = 70;
if (isExample)
{
size = 6;
time = 12;
}
Map map{input, size, time};
return Helper::Dijkstras(map, Index2D{0, 0}, Branch, Goal);
}
OUTPUT2(input)
{
int size = 70;
if (isExample)
size = 6;
Map map{input, size, 0};
int result = Helper::BinarySearch(map, 0, input.size(), ValidPath);
for (auto& index : input)
{
if (index.second == result)
{
std::cout << index.first.x << "," << index.first.y << std::endl;
return result;
}
}
return 0;
}
}
+116
View File
@@ -0,0 +1,116 @@
#include "common/aoc.h"
namespace day19
{
struct Towels
{
std::vector<std::string> patterns;
std::vector<std::string> towels;
};
REGISTER_DAY(2024, Day19, Towels, int64_t);
REGISTER_TEST_EXAMPLE(2024, Day19, ExampleInput, 1, 6);
REGISTER_TEST(2024, Day19, Input, 1, 353);
REGISTER_TEST_EXAMPLE(2024, Day19, ExampleInput, 2, 16);
REGISTER_TEST(2024, Day19, Input, 2, 880877787214477);
READ_INPUT(input)
{
Towels strings;
std::string str;
std::getline(input, str);
std::stringstream ss{str};
while (std::getline(ss, str, ','))
{
if (str.front() == ' ')
str = str.substr(1);
strings.patterns.emplace_back(str);
}
std::getline(input, str);
while (std::getline(input, str))
{
strings.towels.emplace_back(str);
}
return strings;
}
bool HasCombination(const std::string& towel,
const std::vector<std::string>& patterns,
int offset,
std::set<int>& memoization)
{
if (offset == towel.size())
return true;
if (memoization.count(offset) != 0)
return false;
for (auto& other : patterns)
{
if (Helper::StartsWith(std::string_view(towel.c_str() + offset, towel.size() - offset), other))
{
if (HasCombination(towel, patterns, offset + other.size(), memoization))
{
return true;
}
}
}
memoization.emplace(offset);
return false;
}
int64_t AmountOfCombinations(const std::string& towel,
const std::vector<std::string>& patterns,
int offset,
std::map<int, int64_t>& memoization)
{
if (offset == towel.size())
return 1;
auto it = memoization.find(offset);
if (it != memoization.end())
return it->second;
int64_t amount = 0;
for (auto& other : patterns)
{
if (Helper::StartsWith(std::string_view(towel.c_str() + offset, towel.size() - offset), other))
{
amount += AmountOfCombinations(towel, patterns, offset + other.size(), memoization);
}
}
memoization[offset] = amount;
return amount;
}
OUTPUT1(input)
{
int64_t sum = 0;
for (auto& towel : input.towels)
{
std::set<int> memoization;
if (HasCombination(towel, input.patterns, 0, memoization))
sum++;
}
return sum;
}
OUTPUT2(input)
{
int64_t sum = 0;
for (auto& towel : input.towels)
{
std::map<int, int64_t> memoization;
sum += AmountOfCombinations(towel, input.patterns, 0, memoization);
}
return sum;
}
}
+93
View File
@@ -0,0 +1,93 @@
#include "common/aoc.h"
namespace day20
{
REGISTER_DAY(2024, Day20, Array2D<char>, int32_t);
REGISTER_TEST_EXAMPLE(2024, Day20, ExampleInput, 1, 1);
REGISTER_TEST(2024, Day20, Input, 1, 1429);
REGISTER_TEST_EXAMPLE(2024, Day20, ExampleInput, 2, 285);
REGISTER_TEST(2024, Day20, Input, 2, 988931);
READ_INPUT(input)
{
return Input::ReadArray2D(input);
}
// Single path DFS, assumes the path can never hit a dead end
static std::pair<int, std::vector<Index2D>> DFS(const Array2D<char>& map, const Index2D& initial)
{
std::vector<Index2D> path;
Index2D prev = Index2D{-1, -1};
std::pair<int, Index2D> cur{0, initial};
path.emplace_back(initial);
while (map[cur.second] != 'E')
{
for (auto dir : Helper::GetNeighborDirections())
{
Index2D pos = cur.second + dir;
if (map.IsInside(pos))
{
if (pos != prev && map[pos] != '#')
{
prev = cur.second;
cur.first++;
cur.second = pos;
path.emplace_back(pos);
break;
}
}
}
}
return {cur.first, path};
}
int Cheat(const Array2D<char>& map, int cheatTime, int diff)
{
int sum = 0;
std::pair<int, std::vector<Index2D>> path = DFS(map, map.Find('S'));
std::vector<int> distanceToGoal(map.width * map.height, -1);
for (int i = 0; i < path.second.size(); i++)
{
distanceToGoal[map.GetIndex(path.second[i])] = path.second.size() - i - 1;
}
for (int i = 0; i < path.second.size(); i++)
{
for (int y = -cheatTime; y <= cheatTime; y++)
{
int yAbs = std::abs(y);
for (int x = -cheatTime + yAbs; x <= cheatTime - yAbs; x++)
{
int xAbs = std::abs(x);
int cost = i + xAbs + yAbs;
Index2D pos{path.second[i].x + x, path.second[i].y + y};
if (map.IsInside(pos))
{
int index = map.GetIndex(pos);
if (distanceToGoal[index] >= 0)
{
if (cost + distanceToGoal[index] <= path.first - diff)
{
sum++;
}
}
}
}
}
}
return sum;
}
OUTPUT1(input)
{
return Cheat(input, 2, isExample ? 50 : 100);
}
OUTPUT2(input)
{
return Cheat(input, 20, isExample ? 50 : 100);
}
}
+213
View File
@@ -0,0 +1,213 @@
#include "common/aoc.h"
namespace day21
{
REGISTER_DAY(2024, Day21, std::vector<std::string>, int64_t);
REGISTER_TEST_EXAMPLE(2024, Day21, ExampleInput, 1, 126384);
REGISTER_TEST(2024, Day21, Input, 1, 177814);
REGISTER_TEST_EXAMPLE(2024, Day21, ExampleInput, 2, 154115708116294);
REGISTER_TEST(2024, Day21, Input, 2, 220493992841852);
READ_INPUT(input)
{
return Input::ReadLines(input);
}
Index2D KeyPadCoord(char c)
{
if (c == '7')
return Index2D{0, 0};
if (c == '8')
return Index2D{1, 0};
if (c == '9')
return Index2D{2, 0};
if (c == '4')
return Index2D{0, 1};
if (c == '5')
return Index2D{1, 1};
if (c == '6')
return Index2D{2, 1};
if (c == '1')
return Index2D{0, 2};
if (c == '2')
return Index2D{1, 2};
if (c == '3')
return Index2D{2, 2};
if (c == '0')
return Index2D{1, 3};
if (c == 'A')
return Index2D{2, 3};
abort();
}
Index2D MovePadCoord(const Index2D& from, const Index2D& to)
{
if (from.x < to.x)
return Index2D{2, 1};
if (from.x > to.x)
return Index2D{0, 1};
if (from.y < to.y)
return Index2D{1, 1};
if (from.y > to.y)
return Index2D{1, 0};
abort();
}
bool IsSameDir(const Index2D& prev, const Index2D& cur, const Index2D& next)
{
Index2D diffA = cur - prev;
Index2D diffB = next - cur;
if (diffA.x < 0 && diffB.x < 0)
return true;
if (diffA.x > 0 && diffB.x > 0)
return true;
if (diffA.y < 0 && diffB.y < 0)
return true;
if (diffA.y > 0 && diffB.y > 0)
return true;
return false;
}
std::vector<std::vector<Index2D>> PossiblePaths(const Index2D& from, const Index2D& to, bool keypad)
{
std::set<Index2D> visited;
std::queue<std::vector<Index2D>> openPath;
openPath.emplace(std::vector<Index2D>{from});
std::vector<std::vector<Index2D>> paths;
while (!openPath.empty())
{
auto path = openPath.front();
openPath.pop();
visited.emplace(path.back());
if (path.back() == to)
{
paths.emplace_back(path);
continue;
}
for (const auto& dir : Helper::GetNeighborDirections())
{
Index2D newPos = path.back() + dir;
bool keyPadCondition =
keypad && newPos != Index2D{0, 3} && newPos.x >= 0 && newPos.x <= 2 && newPos.y >= 0 && newPos.y <= 3;
bool movePadCondition =
!keypad && newPos != Index2D{0, 0} && newPos.x >= 0 && newPos.x <= 2 && newPos.y >= 0 && newPos.y <= 1;
if (visited.count(newPos) == 0 && (keyPadCondition || movePadCondition))
{
std::vector<Index2D> newPath = path;
if (path.size() == 1)
newPath.emplace_back(newPos);
else if (!IsSameDir(path[path.size() - 2], path.back(), newPos))
newPath.emplace_back(newPos);
else
newPath.back() = newPos;
openPath.emplace(newPath);
}
}
}
return paths;
}
struct State
{
Index2D from;
Index2D to;
int depth;
int64_t amount;
bool operator<(const State& other) const
{
if (from != other.from)
return from < other.from;
if (to != other.to)
return to < other.to;
if (depth != other.depth)
return depth < other.depth;
return amount < other.amount;
}
};
int64_t Move(int maxDepth,
const Index2D& from,
const Index2D& to,
int depth,
int64_t amount,
std::map<State, int64_t>& memoization)
{
if (depth == maxDepth)
return Helper::ManhattanDistance(to, from) + amount;
State state{from, to, depth, amount};
auto it = memoization.find(state);
if (it != memoization.end())
return it->second;
std::vector<std::vector<Index2D>> paths = PossiblePaths(from, to, depth == 0);
int64_t min = std::numeric_limits<int64_t>::max();
for (const auto& path : paths)
{
Index2D moveFrom = Index2D{2, 0};
int64_t sum = 0;
for (int j = 0; j < path.size() - 1; j++)
{
Index2D moveTo = MovePadCoord(path[j], path[j + 1]);
int diff = Helper::ManhattanDistance(path[j], path[j + 1]);
sum += Move(maxDepth, moveFrom, moveTo, depth + 1, diff, memoization);
moveFrom = moveTo;
}
int diff = Helper::ManhattanDistance(path.back(), path.front());
sum += Move(maxDepth, moveFrom, Index2D{2, 0}, depth + 1, amount, memoization);
min = std::min(sum, min);
}
memoization[state] = min;
return min;
}
int64_t Solve(const std::vector<std::string>& input, int depth)
{
std::map<State, int64_t> memoization;
int64_t sum = 0;
for (int i = 0; i < input.size(); i++)
{
Index2D moveFrom = KeyPadCoord('A');
int64_t codeSum = 0;
for (auto c : input[i])
{
Index2D keyPadCoord = KeyPadCoord(c);
codeSum += Move(depth, moveFrom, keyPadCoord, 0, 1, memoization);
moveFrom = keyPadCoord;
}
int64_t codeNum = std::stoi(input[i]);
sum += codeNum * codeSum;
}
return sum;
}
OUTPUT1(input)
{
return Solve(input, 2);
}
OUTPUT2(input)
{
return Solve(input, 25);
}
}
+80
View File
@@ -0,0 +1,80 @@
#include "common/aoc.h"
namespace day22
{
REGISTER_DAY(2024, Day22, std::vector<int>, int64_t);
REGISTER_TEST_EXAMPLE(2024, Day22, ExampleInput, 1, 37990510);
REGISTER_TEST(2024, Day22, Input, 1, 14273043166);
REGISTER_TEST_EXAMPLE(2024, Day22, ExampleInput, 2, 23);
REGISTER_TEST(2024, Day22, Input, 2, 1667);
READ_INPUT(input)
{
return Input::ReadInts(input);
}
int64_t NextNumber(int64_t currentNum)
{
currentNum = ((currentNum * 64) ^ currentNum) % 16777216;
currentNum = ((currentNum / 32) ^ currentNum) % 16777216;
currentNum = ((currentNum * 2048) ^ currentNum) % 16777216;
return currentNum;
}
int GetDiffIndex(const std::vector<int>& vec)
{
int val = 0;
for (int i = vec.size() - 5; i < vec.size() - 1; i++)
{
val *= 19;
val += vec[i + 1] - vec[i] + 9;
}
return val;
}
OUTPUT1(input)
{
int64_t sum = 0;
for (auto num : input)
{
for (int i = 0; i < 2000; i++)
{
num = NextNumber(num);
}
sum += num;
}
return sum;
}
OUTPUT2(input)
{
const int MAX_INDEX = 19 * 19 * 19 * 19;
std::vector<int> monkeySequences(MAX_INDEX, 0);
int best = 0;
for (auto num : input)
{
std::vector<bool> alreadyFoundSequence(MAX_INDEX, false);
std::vector<int> sequence;
sequence.emplace_back(num % 10);
for (int i = 0; i < 2000; i++)
{
num = NextNumber(num);
sequence.emplace_back(num % 10);
if (sequence.size() >= 5)
{
int diffIndex = GetDiffIndex(sequence);
if (!alreadyFoundSequence[diffIndex])
{
alreadyFoundSequence[diffIndex] = true;
monkeySequences[diffIndex] += num % 10;
if (monkeySequences[diffIndex] > best)
best = monkeySequences[diffIndex];
}
}
}
}
return best;
}
}
+105
View File
@@ -0,0 +1,105 @@
#include "common/aoc.h"
namespace day23
{
struct Network
{
std::map<std::string, std::set<std::string>> graph;
};
REGISTER_DAY(2024, Day23, Network, int64_t);
REGISTER_TEST_EXAMPLE(2024, Day23, ExampleInput, 1, 7);
REGISTER_TEST(2024, Day23, Input, 1, 1170);
REGISTER_TEST_EXAMPLE(2024, Day23, ExampleInput, 2, 52);
REGISTER_TEST(2024, Day23, Input, 2, 350);
READ_INPUT(input)
{
Network network;
std::string str;
while (std::getline(input, str))
{
std::stringstream ss{str};
std::string s1, s2;
std::getline(ss, s1, '-');
std::getline(ss, s2, '-');
network.graph[s1].emplace(s2);
network.graph[s2].emplace(s1);
}
return network;
}
void FindLargest(const Network& network, std::set<std::string>& computers, std::set<std::string>& largest)
{
if (computers.size() <= largest.size())
return;
for (const auto& computer : computers)
{
for (const auto& otherComputer : computers)
{
if (otherComputer == computer)
continue;
const auto& neighbors = network.graph.find(computer)->second;
if (neighbors.find(otherComputer) == neighbors.end())
{
// Solution wasn't found, so remove an element and try for a smaller set of computers
for (const auto& node : computers)
{
auto cpy = computers;
cpy.erase(node);
FindLargest(network, cpy, largest);
}
return;
}
}
}
largest = computers;
}
OUTPUT1(input)
{
std::set<std::set<std::string>> groupsOfThree;
for (const auto& computer : input.graph)
{
if (computer.first[0] != 't')
continue;
for (auto it = computer.second.begin(); it != computer.second.end(); it++)
{
auto it2 = it;
it2++;
for (it2; it2 != computer.second.end(); it2++)
{
const auto& paths = input.graph.find(*it)->second;
if (paths.find(*it2) != paths.end())
groupsOfThree.emplace(std::set<std::string>{computer.first, *it, *it2});
}
}
}
return groupsOfThree.size();
}
OUTPUT2(input)
{
std::set<std::string> largest;
for (const auto& computer : input.graph)
{
std::set<std::string> computers = computer.second;
computers.emplace(computer.first);
FindLargest(input, computers, largest);
}
for (const auto& s : largest) std::cout << s << ",";
std::cout << std::endl;
// Some arbitrary solution number to have some form of verification in the code.
// Sum all letters of the computers (with a=0, b=1, etc)
int64_t sum = 0;
for (const auto& s : largest) sum += (s[0] - 'a') + (s[1] - 'a');
return sum;
}
}
+233
View File
@@ -0,0 +1,233 @@
#include "common/aoc.h"
namespace day24
{
struct Gate
{
std::string left;
std::string right;
std::string op;
std::string res;
};
struct Gates
{
std::map<std::string, int> gates;
std::vector<Gate> gateOperation;
};
REGISTER_DAY(2024, Day24, Gates, int64_t);
REGISTER_TEST_EXAMPLE(2024, Day24, ExampleInput, 1, 2024);
REGISTER_TEST(2024, Day24, Input, 1, 57344080719736);
// REGISTER_TEST_EXAMPLE(2024, Day24, ExampleInput, 2, 0);
REGISTER_TEST(2024, Day24, Input, 2, 2328);
READ_INPUT(input)
{
std::string str;
Gates gates;
while (std::getline(input, str))
{
if (str.empty())
break;
std::stringstream ss{str};
std::string name;
int val;
ss >> name >> val;
name.pop_back(); // Remove ':'
gates.gates.emplace(name, val);
}
while (std::getline(input, str))
{
std::stringstream ss{str};
std::string left;
std::string right;
std::string op;
std::string res;
ss >> left >> op >> right >> "->" >> res;
Gate gate{left, right, op, res};
gates.gates.emplace(res, -1);
gates.gateOperation.emplace_back(gate);
}
return gates;
}
int GetVal(Gates& gates, const std::string& wire)
{
return gates.gates[wire];
}
void SetVal(Gates& gates, const std::string& wire, int val)
{
gates.gates[wire] = val;
}
void SetInput(Gates& gates, int64_t x, int64_t y)
{
for (auto& wire : gates.gates)
{
if (wire.first[0] == 'x')
{
wire.second = x & 1;
x >>= 1;
}
if (wire.first[0] == 'y')
{
wire.second = y & 1;
y >>= 1;
}
}
}
std::string IndexToStr(int i)
{
std::string s;
if (i < 10)
s += "0";
s += std::to_string(i);
return s;
}
void SwapOutputs(Gates& gates, const std::string& output1, const std::string& output2, std::set<std::string>& swapped)
{
int i1 = -1;
int i2 = -1;
for (int i = 0; i < gates.gateOperation.size(); i++)
{
if (gates.gateOperation[i].res == output1)
i1 = i;
if (gates.gateOperation[i].res == output2)
i2 = i;
}
std::swap(gates.gateOperation[i1].res, gates.gateOperation[i2].res);
swapped.emplace(gates.gateOperation[i1].res);
swapped.emplace(gates.gateOperation[i2].res);
}
std::string Find(const Gates& gates, const std::string& left, const std::string& right, const std::string& op)
{
for (const auto& gate : gates.gateOperation)
{
if (gate.op == op)
{
if ((gate.left == left && gate.right == right) || (gate.left == right && gate.right == left))
{
return gate.res;
}
}
}
return "";
}
OUTPUT1(input)
{
// Too low: 1972354943
Gates cpy = input;
bool somethingChanged = true;
while (somethingChanged)
{
somethingChanged = false;
for (const auto& gate : cpy.gateOperation)
{
int leftVal = GetVal(cpy, gate.left);
int rightVal = GetVal(cpy, gate.right);
if (leftVal != -1 && rightVal != -1 && GetVal(cpy, gate.res) == -1)
{
somethingChanged = true;
if (gate.op == "AND")
{
SetVal(cpy, gate.res, leftVal & rightVal);
}
else if (gate.op == "OR")
{
SetVal(cpy, gate.res, leftVal | rightVal);
}
else if (gate.op == "XOR")
{
SetVal(cpy, gate.res, leftVal ^ rightVal);
}
}
}
}
int64_t num = 0;
for (const auto& wire : cpy.gates)
{
if (wire.first[0] == 'z')
{
int pos = std::stoi(wire.first.substr(1));
num |= ((int64_t)wire.second << pos);
}
}
return num;
}
OUTPUT2(input)
{
Gates gates = input;
std::set<std::string> outputs;
for (const auto& gate : gates.gateOperation)
{
outputs.emplace(gate.res);
}
std::string carry{Find(gates, "x00", "y00", "AND")};
std::set<std::string> swapped;
for (int i = 1; i < 46; i++)
{
std::string index = IndexToStr(i);
std::string x = "x" + index;
std::string y = "y" + index;
std::string z = "z" + index;
std::string xorIn = Find(gates, x, y, "XOR");
std::string res = Find(gates, carry, xorIn, "XOR");
if (res.empty())
{
for (const auto& output : outputs)
{
std::string res = Find(gates, carry, output, "XOR");
if (res == z)
{
SwapOutputs(gates, xorIn, output, swapped);
xorIn = output;
break;
}
}
}
else if (res != z)
{
SwapOutputs(gates, res, z, swapped);
}
std::string andIn = Find(gates, x, y, "AND");
std::string andXorInCarry = Find(gates, xorIn, carry, "AND");
carry = Find(gates, andIn, andXorInCarry, "OR");
}
for (const auto& swap : swapped)
{
std::cout << swap << ",";
}
std::cout << std::endl;
// Some arbitrary solution number to have some form of verification in the code.
// Sum all letters in the swapped outputs
int64_t sum = 0;
for (const auto& s : swapped)
{
for (char c : s)
{
sum += c = 'a';
}
}
return sum;
}
}
+100
View File
@@ -0,0 +1,100 @@
#include "common/aoc.h"
namespace day25
{
struct KeysLocks
{
int height{0};
std::vector<std::vector<int>> keys;
std::vector<std::vector<int>> locks;
};
REGISTER_DAY(2024, Day25, KeysLocks, int);
REGISTER_TEST_EXAMPLE(2024, Day25, ExampleInput, 1, 3);
REGISTER_TEST(2024, Day25, Input, 1, 3663);
REGISTER_TEST_EXAMPLE(2024, Day25, ExampleInput, 2, 0);
REGISTER_TEST(2024, Day25, Input, 2, 0);
READ_INPUT(input)
{
KeysLocks keysLocks;
bool next = true;
while (next)
{
next = false;
std::string str;
std::vector<int> lockOrKey;
bool lock = true;
int height = 0;
while (std::getline(input, str))
{
next = true;
if (str.empty())
break;
if (lockOrKey.empty())
{
if (str[0] == '.')
lock = false;
lockOrKey = std::vector<int>(str.size(), 0);
}
for (int i = 0; i < str.size(); i++)
{
if (str[i] == '#')
lockOrKey[i]++;
}
height++;
}
if (keysLocks.height == 0)
keysLocks.height = height;
if (!lockOrKey.empty())
{
if (lock)
keysLocks.locks.emplace_back(lockOrKey);
else
keysLocks.keys.emplace_back(lockOrKey);
}
}
return keysLocks;
}
OUTPUT1(input)
{
int64_t sum = 0;
for (const auto& key : input.keys)
{
for (const auto& lock : input.locks)
{
bool matches = true;
for (int i = 0; i < key.size(); i++)
{
if (key[i] + lock[i] > input.height)
{
matches = false;
break;
}
}
if (matches)
sum++;
}
}
return sum;
}
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;
}
}