Compare commits
2 Commits
8936cca9cf
...
9ba9469bb1
Author | SHA1 | Date |
---|---|---|
Bananymous | 9ba9469bb1 | |
Bananymous | 4dbf173ed4 |
|
@ -11,6 +11,7 @@ set(AOC2024_PROJECTS
|
||||||
day10
|
day10
|
||||||
day11
|
day11
|
||||||
day12
|
day12
|
||||||
|
day13
|
||||||
full
|
full
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
#include <BAN/HashMap.h>
|
|
||||||
#include <BAN/HashSet.h>
|
|
||||||
#include <BAN/Vector.h>
|
|
||||||
#include <BAN/Queue.h>
|
#include <BAN/Queue.h>
|
||||||
|
#include <BAN/Sort.h>
|
||||||
|
#include <BAN/Vector.h>
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
@ -28,33 +27,6 @@ struct Position
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PositionHash
|
|
||||||
{
|
|
||||||
constexpr BAN::hash_t operator()(Position state) const
|
|
||||||
{
|
|
||||||
return BAN::hash<u64>{}((u64)state.x << 32 | (u64)state.y);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PosDir
|
|
||||||
{
|
|
||||||
Position pos;
|
|
||||||
u8 dir;
|
|
||||||
|
|
||||||
constexpr bool operator==(PosDir other) const
|
|
||||||
{
|
|
||||||
return pos == other.pos && dir == other.dir;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PosDirHash
|
|
||||||
{
|
|
||||||
constexpr BAN::hash_t operator()(PosDir state) const
|
|
||||||
{
|
|
||||||
return PositionHash{}(state.pos) ^ BAN::hash<uint8_t>{}(state.dir);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Grid2D
|
struct Grid2D
|
||||||
{
|
{
|
||||||
usize width { 0 };
|
usize width { 0 };
|
||||||
|
@ -106,7 +78,8 @@ i64 part1(FILE* fp)
|
||||||
{
|
{
|
||||||
auto map = read_grid2d(fp);
|
auto map = read_grid2d(fp);
|
||||||
|
|
||||||
BAN::HashSet<Position, PositionHash> checked;
|
BAN::Vector<bool> checked;
|
||||||
|
MUST(checked.resize(map.width * map.height, false));
|
||||||
|
|
||||||
i64 result = 0;
|
i64 result = 0;
|
||||||
|
|
||||||
|
@ -115,7 +88,7 @@ i64 part1(FILE* fp)
|
||||||
for (u32 x = 0; x < map.width; x++)
|
for (u32 x = 0; x < map.width; x++)
|
||||||
{
|
{
|
||||||
const auto pos = Position { .x = x, .y = y };
|
const auto pos = Position { .x = x, .y = y };
|
||||||
if (checked.contains(pos))
|
if (checked[map.width * pos.y + pos.x])
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const char target = map.get(pos.x, pos.y);
|
const char target = map.get(pos.x, pos.y);
|
||||||
|
@ -137,9 +110,9 @@ i64 part1(FILE* fp)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (checked.contains(pos))
|
if (checked[map.width * pos.y + pos.x])
|
||||||
continue;
|
continue;
|
||||||
MUST(checked.insert(pos));
|
checked[map.width * pos.y + pos.x] = true;
|
||||||
|
|
||||||
area++;
|
area++;
|
||||||
|
|
||||||
|
@ -160,7 +133,8 @@ i64 part2(FILE* fp)
|
||||||
{
|
{
|
||||||
auto map = read_grid2d(fp);
|
auto map = read_grid2d(fp);
|
||||||
|
|
||||||
BAN::HashSet<Position, PositionHash> checked;
|
BAN::Vector<bool> checked;
|
||||||
|
MUST(checked.resize(map.width * map.height, false));
|
||||||
|
|
||||||
i64 result = 0;
|
i64 result = 0;
|
||||||
|
|
||||||
|
@ -169,67 +143,82 @@ i64 part2(FILE* fp)
|
||||||
for (u32 x = 0; x < map.width; x++)
|
for (u32 x = 0; x < map.width; x++)
|
||||||
{
|
{
|
||||||
const auto pos = Position { .x = x, .y = y };
|
const auto pos = Position { .x = x, .y = y };
|
||||||
if (checked.contains(pos))
|
if (checked[map.width * pos.y + pos.x])
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const char target = map.get(pos.x, pos.y);
|
const char target = map.get(pos.x, pos.y);
|
||||||
|
|
||||||
u32 area = 0;
|
struct PerimeterEntry
|
||||||
BAN::HashMap<PosDir, bool, PosDirHash> perimiter;
|
{
|
||||||
|
Position pos;
|
||||||
|
bool counted;
|
||||||
|
};
|
||||||
|
BAN::Vector<PerimeterEntry> perimiters[4];
|
||||||
|
|
||||||
BAN::Queue<PosDir> checking;
|
struct CheckEntry
|
||||||
|
{
|
||||||
|
Position pos;
|
||||||
|
uint8_t dir;
|
||||||
|
};
|
||||||
|
BAN::Queue<CheckEntry> checking;
|
||||||
MUST(checking.push({ .pos = pos, .dir = 0 }));
|
MUST(checking.push({ .pos = pos, .dir = 0 }));
|
||||||
|
|
||||||
|
u32 area = 0;
|
||||||
|
|
||||||
while (!checking.empty())
|
while (!checking.empty())
|
||||||
{
|
{
|
||||||
auto pos_dir = checking.front();
|
const auto [pos, dir] = checking.front();
|
||||||
checking.pop();
|
checking.pop();
|
||||||
|
|
||||||
if (pos_dir.pos.x >= map.width || pos_dir.pos.y >= map.height || map.get(pos_dir.pos.x, pos_dir.pos.y) != target)
|
if (pos.x >= map.width || pos.y >= map.height || map.get(pos.x, pos.y) != target)
|
||||||
{
|
{
|
||||||
MUST(perimiter.insert(pos_dir, true));
|
MUST(perimiters[dir].emplace_back(pos, true));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (checked.contains(pos_dir.pos))
|
if (checked[map.width * pos.y + pos.x])
|
||||||
continue;
|
continue;
|
||||||
MUST(checked.insert(pos_dir.pos));
|
checked[map.width * pos.y + pos.x] = true;
|
||||||
|
|
||||||
area++;
|
area++;
|
||||||
|
|
||||||
const auto pos = pos_dir.pos;
|
|
||||||
MUST(checking.push({ .pos { .x = pos.x + 1, .y = pos.y + 0 }, .dir = 0 }));
|
MUST(checking.push({ .pos { .x = pos.x + 1, .y = pos.y + 0 }, .dir = 0 }));
|
||||||
MUST(checking.push({ .pos { .x = pos.x - 1, .y = pos.y + 0 }, .dir = 1 }));
|
MUST(checking.push({ .pos { .x = pos.x - 1, .y = pos.y + 0 }, .dir = 1 }));
|
||||||
MUST(checking.push({ .pos { .x = pos.x + 0, .y = pos.y + 1 }, .dir = 2 }));
|
MUST(checking.push({ .pos { .x = pos.x + 0, .y = pos.y + 1 }, .dir = 2 }));
|
||||||
MUST(checking.push({ .pos { .x = pos.x + 0, .y = pos.y - 1 }, .dir = 3 }));
|
MUST(checking.push({ .pos { .x = pos.x + 0, .y = pos.y - 1 }, .dir = 3 }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (auto& perimiter : perimiters)
|
||||||
|
{
|
||||||
|
BAN::sort::sort(perimiter.begin(), perimiter.end(),
|
||||||
|
[](const auto& a, const auto& b) -> bool
|
||||||
|
{
|
||||||
|
if (a.pos.x != b.pos.x)
|
||||||
|
return a.pos.x < b.pos.x;
|
||||||
|
return a.pos.y < b.pos.y;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
for (auto it1 = perimiter.begin(); it1 != perimiter.end(); it1++)
|
for (auto it1 = perimiter.begin(); it1 != perimiter.end(); it1++)
|
||||||
{
|
{
|
||||||
for (auto it2 = BAN::next(it1, 1); it2 != perimiter.end(); it2++)
|
for (auto it2 = it1 + 1; it2 != perimiter.end(); it2++)
|
||||||
{
|
{
|
||||||
if (it1->key.dir != it2->key.dir)
|
if (it1->pos.x != it2->pos.x && it1->pos.y != it2->pos.y)
|
||||||
continue;
|
continue;
|
||||||
if (it1->key.pos.x != it2->key.pos.x && it1->key.pos.y != it2->key.pos.y)
|
if (!it2->counted)
|
||||||
continue;
|
continue;
|
||||||
|
const u32 diff_x = it2->pos.x - it1->pos.x;
|
||||||
auto min_it = it1, max_it = it2;
|
const u32 diff_y = it2->pos.y - it1->pos.y;
|
||||||
if (min_it->key.pos.x > max_it->key.pos.x || min_it->key.pos.y > max_it->key.pos.y)
|
|
||||||
BAN::swap(min_it, max_it);
|
|
||||||
|
|
||||||
if (!max_it->value)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const u32 diff_x = max_it->key.pos.x - min_it->key.pos.x;
|
|
||||||
const u32 diff_y = max_it->key.pos.y - min_it->key.pos.y;
|
|
||||||
if ((diff_x == 0 && diff_y == 1) || (diff_x == 1 && diff_y == 0))
|
if ((diff_x == 0 && diff_y == 1) || (diff_x == 1 && diff_y == 0))
|
||||||
max_it->value = false;
|
it2->counted = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 sides = 0;
|
u32 sides = 0;
|
||||||
for (auto [_, count] : perimiter)
|
for (const auto& perimiter : perimiters)
|
||||||
sides += count;
|
for (auto [_, counted] : perimiter)
|
||||||
|
sides += counted;
|
||||||
|
|
||||||
result += area * sides;
|
result += area * sides;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
set(SOURCES
|
||||||
|
main.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable(aoc2024_day13 ${SOURCES})
|
||||||
|
banan_include_headers(aoc2024_day13 ban)
|
||||||
|
banan_link_library(aoc2024_day13 libc)
|
||||||
|
|
||||||
|
install(TARGETS aoc2024_day13 OPTIONAL)
|
|
@ -0,0 +1,115 @@
|
||||||
|
#include <BAN/Assert.h>
|
||||||
|
#include <BAN/Math.h>
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
using i8 = int8_t;
|
||||||
|
using i16 = int16_t;
|
||||||
|
using i32 = int32_t;
|
||||||
|
using i64 = int64_t;
|
||||||
|
|
||||||
|
using u8 = uint8_t;
|
||||||
|
using u16 = uint16_t;
|
||||||
|
using u32 = uint32_t;
|
||||||
|
using u64 = uint64_t;
|
||||||
|
using usize = size_t;
|
||||||
|
|
||||||
|
struct vec_t { i64 x, y; };
|
||||||
|
|
||||||
|
u64 fewest_tokens(vec_t a, vec_t b, vec_t p)
|
||||||
|
{
|
||||||
|
ASSERT(a.x * b.y != b.x * a.y);
|
||||||
|
|
||||||
|
// n * a.x + m * b.x = p.x <==> m = (p.x - n * a.x) / b.x
|
||||||
|
// n * a.y + m * b.y = p.y
|
||||||
|
|
||||||
|
// n * a.y + ((p.x - n * a.x) / b.x) * b.y = p.y
|
||||||
|
// => n * a.y + (p.x / b.x - n * a.x / b.x) * b.y = p.y
|
||||||
|
// => n * a.y + p.x / b.x * b.y - n * a.x / b.x * b.y = p.y
|
||||||
|
// => n * a.y - n * a.x / b.x * b.y = p.y - p.x / b.x * b.y
|
||||||
|
// => n * a.y * b.x - n * a.x * b.y = p.y * b.x - p.x * b.y
|
||||||
|
// => n * (a.y * b.x - a.x * b.y) = p.y * b.x - p.x * b.y
|
||||||
|
// => n = (p.y * b.x - p.x * b.y) / (a.y * b.x - a.x * b.y)
|
||||||
|
|
||||||
|
const i64 n_num = p.y * b.x - p.x * b.y;
|
||||||
|
const i64 n_den = a.y * b.x - a.x * b.y;
|
||||||
|
if (n_num % n_den)
|
||||||
|
return 0;
|
||||||
|
const i64 n = n_num / n_den;
|
||||||
|
|
||||||
|
const i64 m_num = p.x - n * a.x;
|
||||||
|
const i64 m_den = b.x;
|
||||||
|
if (m_num % m_den)
|
||||||
|
return 0;
|
||||||
|
const i64 m = m_num / m_den;
|
||||||
|
|
||||||
|
return 3 * n + m;
|
||||||
|
}
|
||||||
|
|
||||||
|
i64 part1(FILE* fp)
|
||||||
|
{
|
||||||
|
i64 result = 0;
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
constexpr const char* format =
|
||||||
|
"Button A: X+%" SCNd64 ", Y+%" SCNd64 "\n"
|
||||||
|
"Button B: X+%" SCNd64 ", Y+%" SCNd64 "\n"
|
||||||
|
"Prize: X=%" SCNd64 ", Y=%" SCNd64 "\n";
|
||||||
|
|
||||||
|
vec_t a, b, prize;
|
||||||
|
if (fscanf(fp, format, &a.x, &a.y, &b.x, &b.y, &prize.x, &prize.y) != 6)
|
||||||
|
break;
|
||||||
|
|
||||||
|
result += fewest_tokens(a, b, prize);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
i64 part2(FILE* fp)
|
||||||
|
{
|
||||||
|
i64 result = 0;
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
constexpr const char* format =
|
||||||
|
"Button A: X+%" SCNu64 ", Y+%" SCNu64 "\n"
|
||||||
|
"Button B: X+%" SCNu64 ", Y+%" SCNu64 "\n"
|
||||||
|
"Prize: X=%" SCNu64 ", Y=%" SCNu64 "\n";
|
||||||
|
|
||||||
|
vec_t a, b, prize;
|
||||||
|
if (fscanf(fp, format, &a.x, &a.y, &b.x, &b.y, &prize.x, &prize.y) != 6)
|
||||||
|
break;
|
||||||
|
|
||||||
|
prize.x += 10000000000000;
|
||||||
|
prize.y += 10000000000000;
|
||||||
|
result += fewest_tokens(a, b, prize);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
const char* file_path = "/usr/share/aoc2024/day13_input.txt";
|
||||||
|
|
||||||
|
if (argc >= 2)
|
||||||
|
file_path = argv[1];
|
||||||
|
|
||||||
|
FILE* fp = fopen(file_path, "r");
|
||||||
|
if (fp == nullptr)
|
||||||
|
{
|
||||||
|
perror("fopen");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("part1: %" PRId64 "\n", part1(fp));
|
||||||
|
|
||||||
|
fseek(fp, 0, SEEK_SET);
|
||||||
|
|
||||||
|
printf("part2: %" PRId64 "\n", part2(fp));
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
}
|
Loading…
Reference in New Issue