diff --git a/2021/puzzle-21-01.cc b/2021/puzzle-21-01.cc new file mode 100644 index 0000000..a62d09c --- /dev/null +++ b/2021/puzzle-21-01.cc @@ -0,0 +1,82 @@ +#include +#include +#include +#include +#include + +using UInt = unsigned; + +struct PlayerState +{ + constexpr PlayerState(UInt initial_pos) : pos_(initial_pos) {} + + [[nodiscard]] constexpr auto score() const noexcept -> UInt { return score_; } + [[nodiscard]] constexpr auto pos() const noexcept -> UInt { return pos_; } + + constexpr void move(UInt amount) noexcept + { + amount %= 10; + pos_ += amount; + if (pos_ > 10) { + pos_ -= 10; + } + score_ += pos_; + } + + UInt pos_; + UInt score_{0}; +}; + +struct Die +{ + [[nodiscard]] constexpr auto roll_count() const noexcept -> UInt { return roll_count_; } + + [[nodiscard]] constexpr auto roll() noexcept -> UInt + { + ++roll_count_; + ++current_; + if (current_ == 101) { + current_ = 1; + } + return current_; + } + +private: + UInt current_{0}; + UInt roll_count_{0}; +}; + +auto main() -> int +{ + std::string line; + std::regex re{"Player (\\d) starting position: (\\d+)"}; + + std::vector players; + while (std::getline(std::cin, line) && !line.empty()) { + std::smatch m; + if (!std::regex_search(line, m, re)) { + std::cerr << "Unable to match: " << line << "\n"; + return 1; + } + players.push_back(PlayerState(std::stol(m.str(2)))); + } + + Die die; + bool cont{true}; + while (cont) { + for (auto& player : players) { + UInt move = die.roll() + die.roll() + die.roll(); + player.move(move); + if (player.score() >= 1'000) { + cont = false; + break; + } + } + } + + auto r{ + std::accumulate(players.begin(), players.end(), UInt{die.roll_count()}, + [](auto a, auto const& p) { return a * (p.score() < 1000 ? p.score() : 1); })}; + std::cout << "Multiplied scores " << r << '\n'; + return 0; +} diff --git a/2021/puzzle-21-02.cc b/2021/puzzle-21-02.cc new file mode 100644 index 0000000..f1825c1 --- /dev/null +++ b/2021/puzzle-21-02.cc @@ -0,0 +1,191 @@ +#include +#include +#include +#include +#include +#include +#include + +using UInt = std::uint64_t; + +/* We're going to maintain state in a [0, 21] x [0, 21] grid. Where the position in the grid + * represents (player 1 score, player 2 score). We start in position (0,0). We then do player 1's + * turn removing them from position (0,0) and updating all possible scores, along with the positions + * they can be in. Then from those states we play player 2's turn. If we end up at a score of >= + * 21 we just put the result in the [21,y] or [x,21] value as appropriate. + * + * Then when there's nothing to do in [0,20] x [0,20] we just have to sum up the last column and + * last row to get the number of victories. + * + * So each grid position needs to maintain number of states with that score for each position on the + * track. + */ + +struct State +{ + constexpr static unsigned target_score{21}; + State(unsigned initial_posx, unsigned initial_posy) { get(0, 0, initial_posx, initial_posy) = 1; } + + [[nodiscard]] auto in_play() const noexcept -> bool + { + return std::any_of(data_.begin(), data_.end(), [](UInt x) { return x > 0; }); + } + + [[nodiscard]] auto size1() const noexcept -> UInt { return player1_wins_; } + [[nodiscard]] auto size2() const noexcept -> UInt { return player2_wins_; } + + [[nodiscard]] auto move() const noexcept -> State + { + State result1; + result1.player1_wins_ = player1_wins_; + result1.player2_wins_ = player2_wins_; + + for (unsigned scorex{0}; scorex < target_score; ++scorex) { + for (unsigned scorey{0}; scorey < target_score; ++scorey) { + for (unsigned posx{1}; posx <= loop_size; ++posx) { + for (unsigned posy{1}; posy <= loop_size; ++posy) { + auto states{get(scorex, scorey, posx, posy)}; + if (states == 0) { + continue; + } + for (unsigned die1{1}; die1 < 4; ++die1) { + for (unsigned die2{1}; die2 < 4; ++die2) { + for (unsigned die3{1}; die3 < 4; ++die3) { + result1.move_player1(scorex, scorey, posx, posy, die1 + die2 + die3, states); + } + } + } + } + } + } + } + result1.print(); + + State result2; + result2.player1_wins_ = result1.player1_wins_; + result2.player2_wins_ = result1.player2_wins_; + + for (unsigned scorex{0}; scorex < target_score; ++scorex) { + for (unsigned scorey{0}; scorey < target_score; ++scorey) { + for (unsigned posx{1}; posx <= loop_size; ++posx) { + for (unsigned posy{1}; posy <= loop_size; ++posy) { + auto states{result1.get(scorex, scorey, posx, posy)}; + if (states == 0) { + continue; + } + for (unsigned die1{1}; die1 < 4; ++die1) { + for (unsigned die2{1}; die2 < 4; ++die2) { + for (unsigned die3{1}; die3 < 4; ++die3) { + result2.move_player2(scorex, scorey, posx, posy, die1 + die2 + die3, states); + } + } + } + } + } + } + } + + result2.print(); + + return result2; + } + + void print() const + { + std::cout << std::string(100, '-') << '\n'; + for (unsigned scorex{0}; scorex < target_score; ++scorex) { + for (unsigned scorey{0}; scorey < target_score; ++scorey) { + UInt count{0}; + for (unsigned posx{1}; posx <= loop_size; ++posx) { + for (unsigned posy{1}; posy <= loop_size; ++posy) { + count += get(scorex, scorey, posx, posy); + } + } + std::cout << std::setw(6) << count << ' '; + } + std::cout << '\n'; + } + std::cout << "Player 1 wins: " << player1_wins_ << '\n'; + std::cout << "Player 2 wins: " << player2_wins_ << '\n'; + } + + void move_player1(unsigned scorex, unsigned scorey, unsigned posx, unsigned posy, unsigned amount, + UInt states) + { + auto new_posx{posx + amount % loop_size}; + if (new_posx > loop_size) { + new_posx -= loop_size; + } + auto new_scorex{scorex + new_posx}; + if (new_scorex >= target_score) { + player1_wins_ += states; + } + else { + get(new_scorex, scorey, new_posx, posy) += states; + } + } + void move_player2(unsigned scorex, unsigned scorey, unsigned posx, unsigned posy, unsigned amount, + UInt states) + { + auto new_posy{posy + amount % loop_size}; + if (new_posy > loop_size) { + new_posy -= loop_size; + } + auto new_scorey{scorey + new_posy}; + if (new_scorey >= target_score) { + player2_wins_ += states; + } + else { + get(scorex, new_scorey, posx, new_posy) += states; + } + } + +private: + State() noexcept = default; + + auto get(unsigned scorex, unsigned scorey, unsigned posx, unsigned posy) const noexcept -> UInt + { + return data_[scorey * target_score * loop_size * loop_size + scorex * loop_size * loop_size + + (posy - 1) * loop_size + (posx - 1)]; + } + auto get(unsigned scorex, unsigned scorey, unsigned posx, unsigned posy) noexcept -> UInt& + { + return data_[scorey * target_score * loop_size * loop_size + scorex * loop_size * loop_size + + (posy - 1) * loop_size + (posx - 1)]; + } + + constexpr static unsigned loop_size{10}; + + std::array data_{0}; + UInt player1_wins_{0}; + UInt player2_wins_{0}; +}; + +auto main() -> int +{ + std::string line; + std::regex re{"Player (\\d) starting position: (\\d+)"}; + + std::vector players; + while (std::getline(std::cin, line) && !line.empty()) { + std::smatch m; + if (!std::regex_search(line, m, re)) { + std::cerr << "Unable to match: " << line << "\n"; + return 1; + } + players.push_back(std::stol(m.str(2))); + } + assert(players.size() == 2); + State state(players[0], players[1]); + + while (state.in_play()) { + state = state.move(); + } + + auto player1_wins{state.size1()}; + auto player2_wins{state.size2()}; + std::cout << "Player 1 wins: " << player1_wins << '\n'; + std::cout << "Player 2 wins: " << player2_wins << '\n'; + std::cout << "Max: " << std::max(player1_wins, player2_wins); + return 0; +}