Add solution for "Day 22: Monkey Market", part 2

This commit is contained in:
Stefan Müller 2025-06-10 21:27:09 +02:00
parent f025896e84
commit f312893ed1
4 changed files with 60 additions and 2 deletions

View File

@ -166,6 +166,14 @@ However, it was not immediately obvious to me, that the order of directions in a
Since the length of the target button pattern roughly doubles with each robot along the chain, the algorithm cannot calculate the list of instructions for each robot explicitly. Instead, it considers only the twelve patterns without duplicate buttons that start at the `A` button and end there again. For example, instead of `v<<A>A^>A`, it takes its parts `v<A`, `>A`, and `^>A`, and keeps iterating with those. That means the solver can iteratively calculate the length of each of these short patterns per robot to find the length of the original one at the end of the chain.
### Day 22: Monkey Market
:mag_right: Puzzle: <https://adventofcode.com/2024/day/22>, :white_check_mark: Solver: [`MonkeyMarket.cpp`](src/MonkeyMarket.cpp)
The solver calculates all secret numbers using only fast bit operations to find the solution for part 1.
After each secret number has been calculated, the new price and price change are determined for part 2. For the first occurrence of each sequence of four consecutive changes per monkey buyer, the solver tracks the last price of that sequence. Adding them up per monkey buyer as they are discovered, the maximum of these sums is the target value.
## Thanks
* [Alexander Brouwer](https://github.com/Bromvlieg) for getting the project set up with CMake.

View File

@ -15,12 +15,15 @@
#pragma once
#include <unordered_map>
#include <aoc/framework/Solver-types.hpp>
class MonkeyMarket
: public Solver<int64_t, int64_t>
{
public:
MonkeyMarket(const int inputFileNameSuffix = 0);
virtual const std::string getPuzzleName() const override;
virtual const int getPuzzleDay() const override;
virtual void processDataLine(const std::string& line) override;
@ -28,4 +31,8 @@ class MonkeyMarket
private:
static constexpr int getNSecretNumbers();
static constexpr uint64_t getPruneValue();
static constexpr int getPriceModulo();
uint64_t nBuyers_;
std::unordered_map<uint64_t, std::pair<uint64_t, int>> sequenceResults_;
void updateNewRollingChangeSequence(uint64_t& rollingChangeSequence, const uint64_t change);
};

View File

@ -15,6 +15,11 @@
#include <aoc/MonkeyMarket.hpp>
MonkeyMarket::MonkeyMarket(const int inputFileNameSuffix)
: Solver{ inputFileNameSuffix }, nBuyers_{ 0 }, sequenceResults_{}
{
}
const std::string MonkeyMarket::getPuzzleName() const
{
return "Monkey Market";
@ -28,13 +33,35 @@ const int MonkeyMarket::getPuzzleDay() const
void MonkeyMarket::processDataLine(const std::string& line)
{
uint64_t n{ std::stoull(line) };
uint64_t price{ n % getPriceModulo() };
uint64_t change;
uint64_t newPrice;
uint64_t rollingChangeSequence{ 0 };
for (int i = 0; i < getNSecretNumbers(); i++)
{
n = ((n << 6) ^ n) & getPruneValue();
n = (n >> 5) ^ n;
n = ((n << 11) ^ n) & getPruneValue();
newPrice = n % getPriceModulo();
change = getPriceModulo() + newPrice - price;
price = newPrice;
updateNewRollingChangeSequence(rollingChangeSequence, change);
if (i >= 3)
{
const auto& [it, success] = sequenceResults_.insert({ rollingChangeSequence, { newPrice, nBuyers_ } });
if (!success && it->second.second < nBuyers_)
{
it->second.first += newPrice;
it->second.second = nBuyers_;
if (it->second.first > part2)
{
part2 = it->second.first;
}
}
}
}
part1 += n;
nBuyers_++;
}
void MonkeyMarket::finish()
@ -50,3 +77,15 @@ constexpr uint64_t MonkeyMarket::getPruneValue()
{
return 16777215;
}
constexpr int MonkeyMarket::getPriceModulo()
{
return 10;
}
void MonkeyMarket::updateNewRollingChangeSequence(uint64_t& rollingChangeSequence, const uint64_t change)
{
// Prices vary from -9 to 9, so we can store the price change plus the price modulo of 10, which is a number between
// 1 and 19, as a 5-bit part in this rolling change sequence number and use it as the key in the sequence map.
rollingChangeSequence = ((rollingChangeSequence << 5) & 0b11111111111111111111) | change;
}

View File

@ -389,11 +389,15 @@ TEST_CASE("[MonkeyMarketTests]")
TestContext test;
SECTION("FullData")
{
test.runFull(std::make_unique<MonkeyMarket>(), 19854248602, 0);
test.runFull(std::make_unique<MonkeyMarket>(), 19854248602, 2223);
}
SECTION("ExampleData")
{
test.runExample(std::make_unique<MonkeyMarket>(), 37327623, 0);
test.runExamplePart1(std::make_unique<MonkeyMarket>(), 37327623);
}
SECTION("ExampleData2")
{
test.runExamplePart2(std::make_unique<MonkeyMarket>(2), 23);
}
}