diff --git a/README.md b/README.md
index c81cf60..958d9ec 100644
--- a/README.md
+++ b/README.md
@@ -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`, it takes its parts `vA`, 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: , :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.
diff --git a/include/aoc/MonkeyMarket.hpp b/include/aoc/MonkeyMarket.hpp
index 417073e..9994bf7 100644
--- a/include/aoc/MonkeyMarket.hpp
+++ b/include/aoc/MonkeyMarket.hpp
@@ -15,12 +15,15 @@
#pragma once
+#include
+
#include
class MonkeyMarket
: public Solver
{
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> sequenceResults_;
+ void updateNewRollingChangeSequence(uint64_t& rollingChangeSequence, const uint64_t change);
};
diff --git a/src/MonkeyMarket.cpp b/src/MonkeyMarket.cpp
index 2599b61..4afe975 100644
--- a/src/MonkeyMarket.cpp
+++ b/src/MonkeyMarket.cpp
@@ -15,6 +15,11 @@
#include
+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;
+}
diff --git a/tests/src/TestCases.cpp b/tests/src/TestCases.cpp
index 56fc71b..26aeb57 100644
--- a/tests/src/TestCases.cpp
+++ b/tests/src/TestCases.cpp
@@ -389,11 +389,15 @@ TEST_CASE("[MonkeyMarketTests]")
TestContext test;
SECTION("FullData")
{
- test.runFull(std::make_unique(), 19854248602, 0);
+ test.runFull(std::make_unique(), 19854248602, 2223);
}
SECTION("ExampleData")
{
- test.runExample(std::make_unique(), 37327623, 0);
+ test.runExamplePart1(std::make_unique(), 37327623);
+ }
+ SECTION("ExampleData2")
+ {
+ test.runExamplePart2(std::make_unique(2), 23);
}
}