152 lines
4.0 KiB
C++
152 lines
4.0 KiB
C++
//
|
|
// Created by Grant Horner on 12/4/25.
|
|
//
|
|
|
|
#include <ranges>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "doctest.h"
|
|
#include "utils.h"
|
|
|
|
namespace day4 {
|
|
const std::string sample =
|
|
"..@@.@@@@.\n"
|
|
"@@@.@.@.@@\n"
|
|
"@@@@@.@.@@\n"
|
|
"@.@@@@..@.\n"
|
|
"@@.@@@@.@@\n"
|
|
".@@@@@@@.@\n"
|
|
".@.@.@.@@@\n"
|
|
"@.@@@.@@@@\n"
|
|
".@@@@@@@@.\n"
|
|
"@.@.@@@.@.\n";
|
|
|
|
enum class Point : char {
|
|
Paper = '@',
|
|
Empty = '.',
|
|
};
|
|
|
|
template<utils::StringLike T>
|
|
std::array<char, 8> get_surrounding(const std::vector<T> &lines, const size_t x, const size_t y) noexcept {
|
|
std::array<char, 8> points = {};
|
|
size_t i = 0;
|
|
for (int dy = -1; dy < 2; ++dy) {
|
|
if (y + dy < 0 || y + dy >= lines.size()) { continue; }
|
|
for (int dx = -1; dx < 2; ++dx) {
|
|
if (x + dx < 0 || x + dx >= lines[0].size()) { continue; }
|
|
if (dx == 0 && dy == 0) continue;
|
|
|
|
points[i] = lines[y + dy][x + dx];
|
|
i++;
|
|
}
|
|
}
|
|
|
|
return points;
|
|
}
|
|
|
|
uint32_t solve_first(const std::string_view input) noexcept {
|
|
const auto lines = std::string_view{input}
|
|
| std::views::split('\n')
|
|
| std::views::transform(utils::to_string_view)
|
|
| std::views::filter(utils::non_empty)
|
|
| std::ranges::to<std::vector>();
|
|
|
|
uint32_t accessible = 0;
|
|
|
|
for (size_t y = 0; y < lines.size(); ++y) {
|
|
for (size_t x = 0; x < lines[0].size(); ++x) {
|
|
if (lines[y][x] != '@') continue;
|
|
size_t num_paper = 0;
|
|
for (const char p: get_surrounding(lines, x, y)) {
|
|
if (p == '@') {
|
|
num_paper++;
|
|
}
|
|
if (num_paper == 4) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (num_paper < 4) {
|
|
accessible++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return accessible;
|
|
}
|
|
|
|
template<utils::StringLike T>
|
|
std::vector<std::tuple<size_t, size_t> > remove_paper(const std::vector<T> &lines) {
|
|
std::vector<std::tuple<size_t, size_t> > removable = {};
|
|
|
|
for (size_t y = 0; y < lines.size(); ++y) {
|
|
for (size_t x = 0; x < lines[0].size(); ++x) {
|
|
const auto c = lines[y][x];
|
|
if (c != '@') continue;
|
|
size_t num_paper = 0;
|
|
for (const char p: get_surrounding(lines, x, y)) {
|
|
if (p == '@') {
|
|
num_paper++;
|
|
}
|
|
if (num_paper == 4) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (num_paper < 4) {
|
|
removable.emplace_back(x, y);
|
|
}
|
|
}
|
|
}
|
|
|
|
return removable;
|
|
}
|
|
|
|
uint32_t solve_second(const std::string_view input) {
|
|
std::vector<std::string> lines = std::string_view{input}
|
|
| std::views::split('\n')
|
|
| std::views::transform([](const auto r) {
|
|
return std::string(r.data(), r.size());
|
|
})
|
|
| std::views::filter(utils::non_empty)
|
|
| std::ranges::to<std::vector>();
|
|
bool removed = false;
|
|
uint32_t total_removed = 0;
|
|
do {
|
|
removed = false;
|
|
const auto removable = remove_paper(lines);
|
|
if (!removable.empty()) {
|
|
removed = true;
|
|
}
|
|
for (auto [x, y]: removable) {
|
|
lines[y][x] = '.';
|
|
total_removed++;
|
|
}
|
|
} while (removed);
|
|
|
|
return total_removed;
|
|
}
|
|
|
|
TEST_SUITE_BEGIN("Day 4");
|
|
|
|
TEST_CASE("solves first sample") {
|
|
CHECK(solve_first(sample) == 13);
|
|
}
|
|
|
|
TEST_CASE("solves first puzzle") {
|
|
CHECK(solve_first(utils::read_file("day4input.txt")) == 1626);
|
|
}
|
|
|
|
|
|
TEST_CASE("solves second sample") {
|
|
CHECK(solve_second(sample) == 43);
|
|
}
|
|
|
|
TEST_CASE("solves second puzzle") {
|
|
CHECK(solve_second(utils::read_file("day4input.txt")) == 9173);
|
|
}
|
|
|
|
TEST_SUITE_END();
|
|
}
|