diff --git a/2023/puzzle-14-01.cc b/2023/puzzle-14-01.cc new file mode 100644 index 0000000..9cfa266 --- /dev/null +++ b/2023/puzzle-14-01.cc @@ -0,0 +1,72 @@ +#include +#include +#include +#include +#include +#include + +using UInt = std::uint64_t; + +struct Grid +{ + void push_back(std::string const& row) { rows_.push_back(row); } + + void roll_north() + { + for (auto row{rows_.begin() + 1}; row != rows_.end(); ++row) { + for (std::size_t col{0}; col < row->size(); ++col) { + if (row->at(col) == 'O') { + auto new_row = row - 1; + while (new_row != rows_.begin() && new_row->at(col) == '.') { --new_row; } + if (new_row->at(col) != '.') { ++new_row; } + if (new_row != row) { + new_row->at(col) = 'O'; + row->at(col) = '.'; + } + } + } + } + } + + [[nodiscard]] auto load() const -> UInt + { + UInt load{0}; + for (std::size_t row{0}; row != rows_.size(); ++row) { + auto count = std::count_if(rows_[row].begin(), rows_[row].end(), + [](char c) { return c == 'O'; }); + load += count * (rows_.size() - row); + } + return load; + } + + friend auto operator<<(std::ostream& os, Grid const& grid) -> std::ostream&; + +private: + std::vector rows_; +}; + +auto operator<<(std::ostream& os, Grid const& grid) -> std::ostream& +{ + for (auto const& row : grid.rows_) { os << row << '\n'; } + return os; +} + +auto main() -> int try { + std::string line; + + Grid grid; + while (std::getline(std::cin, line)) { + grid.push_back(line); + } + + std::cout << "Read in grid:\n" << grid; + grid.roll_north(); + std::cout << "Rolled grid:\n" << grid; + std::cout << "Load: " << grid.load() << '\n'; + + return EXIT_SUCCESS; +} +catch (...) { + std::cerr << "Uncaught exception.\n"; + return EXIT_FAILURE; +} diff --git a/2023/puzzle-14-02.cc b/2023/puzzle-14-02.cc new file mode 100644 index 0000000..d004d50 --- /dev/null +++ b/2023/puzzle-14-02.cc @@ -0,0 +1,135 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using UInt = std::uint64_t; + +struct Grid +{ + friend auto operator<<(std::ostream& os, Grid const& grid) -> std::ostream&; + + void push_back(std::string const& row) { rows_.push_back(row); } + + void spin_cycle() + { + for (unsigned i = 0; i < 4; ++i) { + roll_north(); + rotate_right(); + } + } + + [[nodiscard]] auto load() const -> UInt + { + UInt load{0}; + for (std::size_t row{0}; row != rows_.size(); ++row) { + auto const count = std::ranges::count_if(rows_[row].begin(), rows_[row].end(), + [](char c) { return c == 'O'; }); + load += count * (rows_.size() - row); + } + return load; + } + + [[nodiscard]] auto hash() const -> std::size_t + { + return std::hash{}(std::accumulate(rows_.begin(), rows_.end(), std::string(), + [](auto const& a, auto const& b) { + return a + b; + })); + } + + [[nodiscard]] auto operator==(Grid const& rhs) const -> bool { return rows_ == rhs.rows_; } + +private: + void roll_north() + { + for (auto row{rows_.begin() + 1}; row != rows_.end(); ++row) { + for (std::size_t col{0}; col < row->size(); ++col) { + if (row->at(col) == 'O') { + auto new_row = row - 1; + while (new_row != rows_.begin() && new_row->at(col) == '.') { --new_row; } + if (new_row->at(col) != '.') { ++new_row; } + if (new_row != row) { + new_row->at(col) = 'O'; + row->at(col) = '.'; + } + } + } + } + } + + void rotate_right() + { + std::vector new_rows; + new_rows.resize(rows_.front().size()); + for (auto row{rows_.rbegin()}; row != rows_.rend(); ++row) { + for (std::size_t i{0}; i < row->size(); ++i) { + new_rows[i].push_back(row->at(i)); + } + } + std::swap(new_rows, rows_); + } + + std::vector rows_; +}; + +template<> +struct std::hash +{ + [[nodiscard]] auto operator()(Grid const& grid) const noexcept -> std::size_t + { + return grid.hash(); + } +}; + +auto operator<<(std::ostream& os, Grid const& grid) -> std::ostream& +{ + for (auto const& row : grid.rows_) { os << row << '\n'; } + return os; +} + +auto main() -> int try { + std::string line; + std::unordered_map cycle_length; + + Grid grid; + while (std::getline(std::cin, line)) { + grid.push_back(line); + } + + std::cout << "Read in grid:\n" << grid; + constexpr UInt cycle_count{1'000'000'000}; + for (UInt cycle{0}; cycle < cycle_count - 1; ++cycle) { + auto [it, success] = cycle_length.insert({grid, cycle}); + if (!success) { + // We have a loop - so just skip ahead a whole number of loops. + // If I was being clever and my brain was working today I would find the exact correct grid + // in the unordered_map `cycle_length` because we know what it will be, and break here. + // But I'm not and so we'll just clear the cycle_length grid and work out where in the loop + // we are by hand. Its not noticeable even on debug. + std::cout << "Found a loop, cycle " << it->second << " is the same as cycle " << cycle << + '\n'; + UInt const loop_length = cycle - it->second; + UInt const cycles_left = cycle_count - cycle; + UInt const loops_left = cycles_left / loop_length; + cycle += loops_left * loop_length; + // Clear the unordered map because we're being lazy. + cycle_length.clear(); + } + grid.spin_cycle(); + } + grid.spin_cycle(); + std::cout << "Rolled grid:\n" << grid; + std::cout << "Load: " << grid.load() << '\n'; + + return EXIT_SUCCESS; +} +catch (...) { + std::cerr << "Uncaught exception.\n"; + return EXIT_FAILURE; +}