146 lines
3.2 KiB
C++
146 lines
3.2 KiB
C++
#include <algorithm>
|
|
#include <concepts>
|
|
#include <cstdint>
|
|
#include <iostream>
|
|
#include <iterator>
|
|
#include <map>
|
|
#include <set>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <tuple>
|
|
#include <vector>
|
|
|
|
#include <aocpp/Startup.hpp>
|
|
#include <aocpp/Coord.hpp>
|
|
|
|
using namespace aocpp;
|
|
|
|
namespace {
|
|
|
|
struct Map {
|
|
std::vector<std::string> rows;
|
|
|
|
auto operator[](Coord c) -> char& { return rows[c.y][c.x]; }
|
|
auto operator[](Coord c) const -> char { return rows[c.y][c.x]; }
|
|
|
|
static auto Parse(std::istream & in) -> Map {
|
|
Map result;
|
|
std::string line;
|
|
while (std::getline(in, line)) {
|
|
result.rows.emplace_back(std::move(line));
|
|
}
|
|
return {result};
|
|
}
|
|
};
|
|
|
|
auto FindBugs(Map const& map) {
|
|
std::vector<Coord> result;
|
|
|
|
std::int64_t w = map.rows[0].size();
|
|
std::int64_t h = map.rows.size();
|
|
|
|
for (std::int64_t x = 0; x < w; x++) {
|
|
for (std::int64_t y = 0; y < h; y++) {
|
|
Coord const c = {x,y};
|
|
if (map[c] == '#') result.push_back(c);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
struct Neighbor1 {
|
|
auto operator()(Coord c, auto f) const -> void {
|
|
if (c.x > 0) f(Left(c));
|
|
if (c.x < 4) f(Right(c));
|
|
if (c.y > 0) f(Up(c));
|
|
if (c.y < 4) f(Down(c));
|
|
}
|
|
};
|
|
|
|
struct Neighbor2 {
|
|
using C3 = std::pair<Coord, std::int64_t>;
|
|
auto operator()(C3 cd, auto f) const -> void {
|
|
auto const [c,d] = cd;
|
|
|
|
auto left_neighbors = [&](auto k_, auto k) {
|
|
auto c_ = k_(c);
|
|
if (c_.x == 1 && c_.y == 0) {
|
|
for (std::int64_t yi = -2; yi <= 2; yi++) {
|
|
f({k({2,yi}),d+1});
|
|
}
|
|
} else if (c_.x > -2) {
|
|
f({k(Left(c_)),d});
|
|
} else {
|
|
f({k({-1,0}),d-1});
|
|
}
|
|
};
|
|
auto id = [](Coord i) { return i; };
|
|
left_neighbors(id, id);
|
|
left_neighbors(Turn180, Turn180);
|
|
left_neighbors(CW, CCW);
|
|
left_neighbors(CCW, CW);
|
|
}
|
|
};
|
|
|
|
template<typename F, typename C>
|
|
auto Step(std::vector<C> const& bugs, F with_neighbors = F{})
|
|
{
|
|
std::map<C, int> adjacent;
|
|
for (auto const& x : bugs) {
|
|
with_neighbors(x, [&adjacent](C n) -> void { adjacent[n]++; });
|
|
}
|
|
|
|
std::vector<C> result;
|
|
for (auto const& [k,v] : adjacent) {
|
|
switch (v) {
|
|
case 1:
|
|
result.push_back(k);
|
|
break;
|
|
case 2:
|
|
if (!std::binary_search(bugs.begin(), bugs.end(), k)) {
|
|
result.push_back(k);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
auto Bio(std::vector<Coord> const& bugs) -> std::uint32_t {
|
|
std::uint32_t result = 0;
|
|
for (auto const& c : bugs) {
|
|
result |= 1U << (5 * c.y + c.x);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
auto Part1(std::vector<Coord> const& bugs) -> std::uint32_t {
|
|
auto cursor = bugs;
|
|
std::set<std::uint32_t> seen;
|
|
std::uint32_t bio;
|
|
while (seen.insert(bio = Bio(cursor)).second) {
|
|
cursor = Step<Neighbor1>(cursor);
|
|
}
|
|
return bio;
|
|
}
|
|
|
|
auto Part2(std::vector<Coord> const& bugs) {
|
|
std::vector<std::pair<Coord,std::int64_t>> bugs2;
|
|
for (auto const& [x,y] : bugs) {
|
|
bugs2.push_back({{x-2,y-2},0});
|
|
}
|
|
for (int i = 0; i < 200; i++) {
|
|
bugs2 = Step<Neighbor2>(bugs2);
|
|
}
|
|
return bugs2.size();
|
|
}
|
|
|
|
} // namespace
|
|
|
|
auto main(int argc, char** argv) -> int {
|
|
auto const bugs = FindBugs(Map::Parse(Startup(argc, argv)));
|
|
std::cout << "Part 1: " << Part1(bugs) << std::endl;
|
|
std::cout << "Part 2: " << Part2(std::move(bugs)) << std::endl;
|
|
}
|