Files
advent-of-code/2022/puzzle-23-01.cc

205 lines
5.4 KiB
C++

//
// Created by Matthew Gretton-Dann on 16/12/2022.
//
#include <array>
#include <iostream>
#include <list>
#include <map>
#include <numeric>
#include <stdexcept>
#include <utility>
#include <vector>
using Int = std::int64_t;
using UInt = std::uint64_t;
using Point = std::pair<Int, Int>;
using Grid = std::vector<std::string>;
using Dirs = std::list<std::array<Point, 3>>;
constexpr Point north{0, -1};
constexpr Point north_east{1, -1};
constexpr Point east{1, 0};
constexpr Point south_east{1, 1};
constexpr Point south{0, 1};
constexpr Point south_west{-1, 1};
constexpr Point west{-1, 0};
constexpr Point north_west{-1, -1};
auto next_state(Grid const& grid, Dirs const& dirs) -> Grid
{
constexpr std::array<Point, 8> all_dirs{north, north_east, east, south_east,
south, south_west, west, north_west};
UInt const width{grid.at(0).size()};
UInt const height{grid.size()};
Int num_pts{0};
Grid result(height, std::string(width, '.'));
// We have designed the grid so we shouldn't need to check the edges.
for (unsigned y{1}; y < height - 1; ++y) {
for (unsigned x{1}; x < width - 1; ++x) {
if (grid.at(y).at(x) == '.') {
continue;
}
++num_pts;
if (std::all_of(all_dirs.begin(), all_dirs.end(), [&x, &y, &grid](auto const& dir) {
return grid.at(dir.second + y).at(dir.first + x) == '.';
})) {
result.at(y).at(x) = '#';
continue;
}
bool moved{false};
Point direction{0, 0};
for (auto const& ds : dirs) {
if (std::all_of(ds.begin(), ds.end(), [&x, &y, &grid](auto const& d) {
return grid.at(y + d.second).at(x + d.first) == '.';
})) {
moved = true;
direction = ds[1];
}
if (moved) {
break;
}
}
char const coord{result.at(y + direction.second).at(x + direction.first)};
if (result.at(y + direction.second).at(x + direction.first) == '.') {
result.at(y + direction.second).at(x + direction.first) =
static_cast<char>('#' + (direction.second * 4) + direction.first);
}
else {
result.at(y).at(x) = '#';
int dx{0};
int dy{0};
if (coord != 'M') {
switch (coord) {
case '#' - 4 - 1:
dx = 1;
dy = 1;
break;
case '#' - 4:
dx = 0;
dy = 1;
break;
case '#' - 4 + 1:
dx = -1;
dy = 1;
break;
case '#' - 1:
dx = 1;
dy = 0;
break;
case '#' + 1:
dx = -1;
dy = 0;
break;
case '#' + 4 - 1:
dx = 1;
dy = -1;
break;
case '#' + 4:
dx = 0;
dy = -1;
break;
case '#' + 4 + 1:
dx = -1;
dy = -1;
break;
default:
abort();
}
result.at(y + direction.second).at(x + direction.first) = 'M';
result.at(y + direction.second + dy).at(x + direction.first + dx) = '#';
}
}
}
}
for (unsigned y{0}; y < height; ++y) {
for (unsigned x{0}; x < width; ++x) {
if (result.at(y).at(x) == 'M') {
result.at(y).at(x) = '.';
}
else if (result.at(y).at(x) != '.') {
result.at(y).at(x) = '#';
--num_pts;
}
}
}
assert(num_pts == 0);
return result;
}
auto calc_score(Grid const& grid) -> UInt
{
UInt const width{grid.at(0).size()};
UInt const height{grid.size()};
UInt num_pts{0};
UInt min_x{std::numeric_limits<UInt>::max()};
UInt min_y{std::numeric_limits<UInt>::max()};
UInt max_x{0};
UInt max_y{0};
for (UInt y{0}; y < height; ++y) {
for (UInt x{0}; x < width; ++x) {
if (grid[y][x] == '#') {
min_x = std::min(min_x, x);
min_y = std::min(min_y, y);
max_x = std::max(max_x, x);
max_y = std::max(max_y, y);
++num_pts;
}
}
}
return (max_x - min_x + 1) * (max_y - min_y + 1) - num_pts;
}
using namespace std::string_literals;
auto main() -> int
{
std::string line;
Grid grid;
Dirs dirs_to_consider = {{north_west, north, north_east},
{south_east, south, south_west},
{south_west, west, north_west},
{north_east, east, south_east}};
constexpr UInt num_rounds{10};
// We surround each line of the grid with extra dots so we don't have to resize as we go along.
std::size_t max_row_length{0};
std::string const padding(num_rounds + 1, '.');
while (std::getline(std::cin, line)) {
std::string result{padding};
result += line;
result += padding;
grid.push_back(result);
max_row_length = std::max(max_row_length, result.size());
}
for (auto& s : grid) {
s.resize(max_row_length, '.');
}
grid.insert(grid.begin(), num_rounds + 1, std::string(max_row_length, '.'));
grid.insert(grid.end(), num_rounds + 1, std::string(max_row_length, '.'));
for (UInt i{0}; i < num_rounds; ++i) {
std::cout << "\n\nStep " << i << "\n";
for (auto const& r : grid) {
std::cout << r << "\n";
}
grid = next_state(grid, dirs_to_consider);
dirs_to_consider.push_back(dirs_to_consider.front());
dirs_to_consider.pop_front();
}
UInt const score{calc_score(grid)};
std::cout << "Score: " << score << "\n";
return EXIT_SUCCESS;
}