2023-01-18 15:24:18 -08:00
|
|
|
#include <cstdint>
|
|
|
|
#include <iostream>
|
2023-03-22 14:02:29 -07:00
|
|
|
#include <optional>
|
2023-01-18 15:24:18 -08:00
|
|
|
#include <set>
|
|
|
|
#include <sstream>
|
|
|
|
#include <tuple>
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
#include <doctest.h>
|
|
|
|
|
|
|
|
#include <aocpp/Coord.hpp>
|
|
|
|
#include <aocpp/Grid.hpp>
|
|
|
|
#include <aocpp/Startup.hpp>
|
|
|
|
|
|
|
|
using aocpp::Coord;
|
|
|
|
using aocpp::Grid;
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
2023-03-22 14:02:29 -07:00
|
|
|
auto Parse(std::istream & in) -> std::tuple<Coord, std::vector<Coord>, Coord, Grid>
|
2023-01-18 15:24:18 -08:00
|
|
|
{
|
2023-03-27 14:11:40 -07:00
|
|
|
std::tuple<Coord, std::vector<Coord>, Coord, Grid> result {{},{},{},Grid::Parse(in)};
|
|
|
|
// I'd use a structured binding here, but not all current compilers
|
|
|
|
// supports capture of the resulting bindings in the lambda below
|
|
|
|
auto& start1 = std::get<0>(result);
|
|
|
|
auto& starts2 = std::get<1>(result);
|
|
|
|
auto& end = std::get<2>(result);
|
|
|
|
auto& grid = std::get<3>(result);
|
2023-01-18 15:24:18 -08:00
|
|
|
|
2024-09-05 20:10:34 -07:00
|
|
|
for (auto [c, v] : grid) {
|
2023-01-18 15:24:18 -08:00
|
|
|
switch (v) {
|
2023-03-22 14:02:29 -07:00
|
|
|
case 'S': grid[c] = 'a'; start1 = c; break;
|
2023-01-18 15:24:18 -08:00
|
|
|
case 'E': grid[c] = 'z'; end = c; break;
|
2023-03-22 14:02:29 -07:00
|
|
|
case 'a': starts2.push_back(c); break;
|
2023-01-18 15:24:18 -08:00
|
|
|
}
|
2024-09-05 20:10:34 -07:00
|
|
|
}
|
2023-01-18 15:24:18 -08:00
|
|
|
|
2023-03-27 14:11:40 -07:00
|
|
|
return result;
|
2023-01-18 15:24:18 -08:00
|
|
|
}
|
|
|
|
|
2023-03-22 14:02:29 -07:00
|
|
|
auto Solve(std::vector<Coord> starts, Coord end, Grid const& grid) -> std::optional<std::int64_t>
|
2023-01-18 15:24:18 -08:00
|
|
|
{
|
|
|
|
std::vector<Coord> next_layer;
|
|
|
|
std::set<Coord> seen;
|
2023-03-27 14:11:40 -07:00
|
|
|
std::int64_t counter {};
|
2023-01-18 15:24:18 -08:00
|
|
|
|
2023-03-22 14:02:29 -07:00
|
|
|
while (!starts.empty()) {
|
2023-03-27 14:11:40 -07:00
|
|
|
counter++;
|
|
|
|
for (auto const& here : starts) {
|
2023-01-18 15:24:18 -08:00
|
|
|
if (!seen.insert(here).second) continue;
|
2023-03-27 14:11:40 -07:00
|
|
|
for (auto const next : {Up(here), Down(here), Left(here), Right(here)}) {
|
|
|
|
if (grid.contains(next) && grid[next] <= grid[here] + 1) {
|
|
|
|
if (next == end) { return counter; }
|
|
|
|
next_layer.push_back(next);
|
|
|
|
}
|
2023-01-18 15:24:18 -08:00
|
|
|
}
|
|
|
|
}
|
2023-03-22 14:02:29 -07:00
|
|
|
std::swap(starts, next_layer);
|
2023-03-27 14:11:40 -07:00
|
|
|
next_layer.clear();
|
2023-01-18 15:24:18 -08:00
|
|
|
}
|
|
|
|
|
2023-03-22 14:02:29 -07:00
|
|
|
return {};
|
2023-01-18 15:24:18 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
TEST_SUITE("2022-12 examples") {
|
|
|
|
TEST_CASE("documented example") {
|
|
|
|
std::istringstream in {
|
|
|
|
"Sabqponm\n"
|
|
|
|
"abcryxxl\n"
|
|
|
|
"accszExk\n"
|
|
|
|
"acctuvwj\n"
|
|
|
|
"abdefghi\n"
|
|
|
|
};
|
2023-03-22 14:02:29 -07:00
|
|
|
auto [start1, starts2, end, grid] = Parse(in);
|
|
|
|
CHECK(31 == Solve({start1}, end, grid));
|
|
|
|
CHECK(29 == Solve(std::move(starts2), end, grid));
|
2023-01-18 15:24:18 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-31 09:15:15 -08:00
|
|
|
auto Main(std::istream & in, std::ostream & out) -> void
|
2023-01-18 15:24:18 -08:00
|
|
|
{
|
2023-03-22 14:02:29 -07:00
|
|
|
auto [start1, starts2, end, grid] = Parse(in);
|
|
|
|
out << "Part 1: " << Solve({start1}, end, grid).value_or(-1) << std::endl;
|
|
|
|
out << "Part 2: " << Solve(std::move(starts2), end, grid).value_or(-1) << std::endl;
|
2023-01-18 15:24:18 -08:00
|
|
|
}
|