aoc2024: Optimize day12

There is actually no need for hash maps
This commit is contained in:
Bananymous 2024-12-14 03:58:11 +02:00
parent 4dbf173ed4
commit 9ba9469bb1
1 changed files with 54 additions and 65 deletions

View File

@ -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 it1 = perimiter.begin(); it1 != perimiter.end(); it1++) for (auto& perimiter : perimiters)
{ {
for (auto it2 = BAN::next(it1, 1); it2 != perimiter.end(); it2++) 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++)
{ {
if (it1->key.dir != it2->key.dir) for (auto it2 = it1 + 1; it2 != perimiter.end(); it2++)
continue; {
if (it1->key.pos.x != it2->key.pos.x && it1->key.pos.y != it2->key.pos.y) if (it1->pos.x != it2->pos.x && it1->pos.y != it2->pos.y)
continue; continue;
if (!it2->counted)
auto min_it = it1, max_it = it2; continue;
if (min_it->key.pos.x > max_it->key.pos.x || min_it->key.pos.y > max_it->key.pos.y) const u32 diff_x = it2->pos.x - it1->pos.x;
BAN::swap(min_it, max_it); const u32 diff_y = it2->pos.y - it1->pos.y;
if ((diff_x == 0 && diff_y == 1) || (diff_x == 1 && diff_y == 0))
if (!max_it->value) it2->counted = false;
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))
max_it->value = 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;
} }