Skip to content

Commit 229c96a

Browse files
committed
2024 day 22: solve part 2
This is very slow: about 2 seconds in fast mode on xrb
1 parent f893ad1 commit 229c96a

File tree

5 files changed

+118
-13
lines changed

5 files changed

+118
-13
lines changed

2024/answer_tests/day22/example1.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
Day 22:
22
37327623
3+
24

2024/answer_tests/day22/example2.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Day 22:
2+
37990510
3+
23

2024/input/day22/example2.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
1
2+
2
3+
3
4+
2024

2024/src/day22.cpp

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,24 @@
1111
#include <iostream> // for cout
1212

1313
int main(int argc, char **argv) {
14-
std::ifstream infile = aoc::parse_args(argc, argv).infile;
14+
auto args = aoc::parse_args(argc, argv);
1515

16-
long total = 0;
17-
long secret;
18-
while (infile >> secret) {
16+
std::uint64_t part_1 = 0;
17+
std::uint32_t secret;
18+
for (int monkey_number = 0; args.infile >> secret; ++monkey_number) {
19+
aoc::day22::PriceSequence sequence{secret, monkey_number};
1920
for (int i = 0; i < 2000; ++i) {
20-
aoc::day22::evolve_secret(secret);
21+
sequence.evolve(i);
2122
}
22-
total += secret;
23+
part_1 += sequence.get_secret();
24+
}
25+
std::cout << part_1 << "\n";
26+
int part_2 = aoc::day22::PriceSequence::find_best_sell_sequence();
27+
std::cout << part_2 << "\n";
28+
if (args.input_type == aoc::InputType::MAIN && part_2 >= 1450) {
29+
std::cerr << "\033[1;31mERROR:\033[0m result for part 2 is too high\n";
30+
return 1;
2331
}
24-
std::cout << total << "\n";
2532

2633
return 0;
2734
}

2024/src/day22.hpp

Lines changed: 96 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,65 @@
88
#ifndef DAY22_HPP_A3Y597BB
99
#define DAY22_HPP_A3Y597BB
1010

11+
#include "lib.hpp" // for DEBUG
12+
#include "unit_test/pretty_print.hpp" // for repr
13+
#include "util/hash.hpp" // for make_hash
14+
15+
#include <algorithm> // for ranges::max_element
16+
#include <array> // for array
17+
#include <cstddef> // for size_t
18+
#include <cstdint> // for uint8_t, uint32_t, uint64_t
19+
#include <functional> // for hash
20+
#include <iomanip> // for setw
21+
#include <iostream> // for cerr
22+
#include <unordered_map> // for unordered_map
23+
#include <utility> // for pair (unordered_map)
24+
25+
namespace aoc::day22 {
26+
struct ChangeSequence : public std::array<std::int8_t, 4> {};
27+
} // namespace aoc::day22
28+
29+
template <>
30+
struct std::hash<aoc::day22::ChangeSequence> {
31+
std::size_t
32+
operator()(const aoc::day22::ChangeSequence &changes) const noexcept {
33+
// random number (hexdump -n8 -e '"0x" 8/1 "%02x" "ull\n"'</dev/urandom)
34+
std::size_t seed = 0xc17a07615bebc283ull;
35+
util::make_hash(seed, changes[0], changes[1], changes[2], changes[3]);
36+
return seed;
37+
}
38+
};
39+
1140
namespace aoc::day22 {
1241

13-
void xorshift(long &secret, int shift) {
14-
long tmp = secret;
42+
ChangeSequence add_price_change(const ChangeSequence &changes,
43+
std::int8_t new_change) {
44+
return ChangeSequence{changes[1], changes[2], changes[3], new_change};
45+
}
46+
47+
class PriceSequence {
48+
std::uint32_t secret;
49+
int monkey_number;
50+
ChangeSequence curr_changes{};
51+
static std::unordered_map<ChangeSequence,
52+
std::unordered_map<int, std::int8_t>>
53+
sequence_prices;
54+
55+
void xorshift(int shift);
56+
57+
public:
58+
explicit PriceSequence(std::uint32_t initial_secret, int monkey_number)
59+
: secret(initial_secret), monkey_number(monkey_number) {}
60+
61+
void evolve(int iter);
62+
std::uint32_t get_secret() const { return secret; }
63+
std::int8_t get_price() const { return secret % 10; }
64+
65+
static int find_best_sell_sequence();
66+
};
67+
68+
void PriceSequence::xorshift(int shift) {
69+
std::uint32_t tmp = secret;
1570
if (shift < 0) {
1671
tmp >>= -shift;
1772
} else {
@@ -20,12 +75,47 @@ void xorshift(long &secret, int shift) {
2075
secret = (secret ^ tmp) & 0xFF'FF'FF;
2176
}
2277

23-
void evolve_secret(long &secret) {
24-
xorshift(secret, +6);
25-
xorshift(secret, -5);
26-
xorshift(secret, +11);
78+
void PriceSequence::evolve(int iter) {
79+
// save previous price
80+
std::int8_t old_price = secret % 10;
81+
xorshift(+6);
82+
xorshift(-5);
83+
xorshift(+11);
84+
std::int8_t new_price = secret % 10;
85+
curr_changes = add_price_change(curr_changes, new_price - old_price);
86+
if (iter >= 3) {
87+
// try_emplace does nothing if the key already exists
88+
sequence_prices[curr_changes].try_emplace(monkey_number, new_price);
89+
}
2790
}
2891

92+
int PriceSequence::find_best_sell_sequence() {
93+
const auto sell_sequence_value = [](const auto &entry) -> int {
94+
int total = 0;
95+
for (const auto &[_, price] : entry.second) {
96+
total += price;
97+
}
98+
return total;
99+
};
100+
auto it =
101+
std::ranges::max_element(sequence_prices, {}, sell_sequence_value);
102+
if constexpr (aoc::DEBUG) {
103+
std::cerr << "best sequence: "
104+
<< pretty_print::repr<std::array<std::int8_t, 4>>(
105+
it->first, {.char_as_number = true})
106+
<< "\n";
107+
std::cerr << "prices:\n";
108+
for (const auto &[monkey, price] : it->second) {
109+
std::cerr << std::setw(5) << monkey << ": " << aoc::as_number(price)
110+
<< "\n";
111+
}
112+
}
113+
return sell_sequence_value(*it);
114+
}
115+
116+
std::unordered_map<ChangeSequence, std::unordered_map<int, std::int8_t>>
117+
PriceSequence::sequence_prices{};
118+
29119
} // namespace aoc::day22
30120

31121
#endif /* end of include guard: DAY22_HPP_A3Y597BB */

0 commit comments

Comments
 (0)