aocpp/2017/22.cpp

150 lines
3.0 KiB
C++
Raw Normal View History

2024-09-02 14:12:42 -07:00
#include <cstddef>
#include <cstdint>
2024-09-02 15:20:25 -07:00
#include <iostream>
2024-09-02 14:12:42 -07:00
#include <stdexcept>
2024-09-02 15:20:25 -07:00
#include <sstream>
2024-09-02 14:12:42 -07:00
#include <unordered_map>
#include <unordered_set>
#include <doctest.h>
#include <aocpp/Grid.hpp>
#include <aocpp/Startup.hpp>
#include <aocpp/Coord.hpp>
2024-09-02 15:20:25 -07:00
auto Part1(aocpp::Grid const &input, std::int64_t reps = 10'000) -> std::int64_t
2024-09-02 14:12:42 -07:00
{
std::unordered_set<aocpp::Coord> infected;
// Initialize set of infected locations
2024-09-05 20:10:34 -07:00
for (auto [pos, cell] : input)
2024-09-02 15:20:25 -07:00
{
2024-09-02 14:12:42 -07:00
if (cell == '#')
{
infected.insert(pos);
2024-09-02 15:20:25 -07:00
}
2024-09-05 20:10:34 -07:00
}
2024-09-02 14:12:42 -07:00
// Start in the middle
aocpp::Coord pos;
pos.x = input.Cols() / 2;
pos.y = input.Rows() / 2;
// Start going "up"
aocpp::Coord vel{.x = 0, .y = -1};
// Count the number of iterations that cause an infection
std::uint64_t infections = 0;
2024-09-02 15:20:25 -07:00
while (reps --> 0)
2024-09-02 14:12:42 -07:00
{
2024-09-02 15:20:25 -07:00
auto const [it, added] = infected.insert(pos);
if (added)
2024-09-02 14:12:42 -07:00
{
// was clean
2024-09-02 15:20:25 -07:00
vel = CCW(vel); // turn "left"
2024-09-02 14:12:42 -07:00
infections++;
}
else
{
// was infected
2024-09-02 15:20:25 -07:00
vel = CW(vel); // turn "right"
2024-09-02 14:12:42 -07:00
infected.erase(it); // clean
}
pos += vel; // advance
}
return infections;
}
2024-09-02 15:20:25 -07:00
auto Part2(aocpp::Grid const &input, std::int64_t reps = 10'000'000) -> std::int64_t
2024-09-02 14:12:42 -07:00
{
std::unordered_map<aocpp::Coord, char> cells;
// Initialize set of infected locations
2024-09-05 20:10:34 -07:00
for (auto [pos, cell] : input)
2024-09-02 15:20:25 -07:00
{
2024-09-02 14:12:42 -07:00
if (cell == '#')
{
cells.try_emplace(pos, '#');
2024-09-02 15:20:25 -07:00
}
2024-09-05 20:10:34 -07:00
}
2024-09-02 14:12:42 -07:00
// Start in the middle
aocpp::Coord pos;
pos.x = input.Cols() / 2;
pos.y = input.Rows() / 2;
// Start going "up"
aocpp::Coord vel{.x = 0, .y = -1};
// Count the number of iterations that cause an infection
std::uint64_t infections = 0;
2024-09-02 15:20:25 -07:00
while (reps --> 0)
2024-09-02 14:12:42 -07:00
{
auto const [it, added] = cells.try_emplace(pos, 'W'); // clean becomes weakened
if (added)
{
// already inserted 'W'
vel = CCW(vel); // turn "left"
}
else
{
switch (it->second)
{
case 'W':
// no turn
it->second = '#';
infections++;
break;
case '#':
vel = CW(vel); // turn "right"
it->second = 'F';
break;
case 'F':
vel = Turn180(vel);
cells.erase(it); // clean
break;
default:
throw std::logic_error{"unexpected cell in steps"};
}
}
pos += vel; // advance
}
return infections;
}
TEST_SUITE("2017-22 examples")
{
TEST_CASE("part 1")
{
2024-09-02 15:20:25 -07:00
std::istringstream in{
"..#\n"
"#..\n"
"...\n"};
auto const input = aocpp::Grid::Parse(in);
CHECK(5 == Part1(input, 7));
CHECK(41 == Part1(input, 70));
CHECK(5587 == Part1(input, 10'000));
2024-09-02 14:12:42 -07:00
}
TEST_CASE("part 2")
{
2024-09-02 15:20:25 -07:00
std::istringstream in{
"..#\n"
"#..\n"
"...\n"};
auto const input = aocpp::Grid::Parse(in);
CHECK(26 == Part2(input, 100));
CHECK(2'511'944 == Part2(input, 10'000'000));
2024-09-02 14:12:42 -07:00
}
}
auto Main(std::istream &in, std::ostream &out) -> void
{
auto const input = aocpp::Grid::Parse(in);
out << "Part 1: " << Part1(input) << std::endl;
out << "Part 2: " << Part2(input) << std::endl;
}