|
2 | 2 | require 'parallel'
|
3 | 3 |
|
4 | 4 | class Day22 < Day # >
|
| 5 | + attr_reader :buyers |
| 6 | + |
| 7 | + def initialize(*args) |
| 8 | + super |
| 9 | + @buyers = |
| 10 | + Parallel |
| 11 | + .map(input.scan(/\d+/)) { |initial_secret_number| |
| 12 | + Buyer.new(initial_secret_number.to_i) |
| 13 | + } |
| 14 | + puts "initialized" if ENV['DEBUG'] |
| 15 | + end |
5 | 16 |
|
6 | 17 | # @example
|
| 18 | + # day = Day22.new('1 10 100 2024') |
7 | 19 | # day.part1 #=> 37327623
|
8 | 20 | def part1
|
9 |
| - initial_secret_numbers = input.split("\n").map(&:to_i) |
10 |
| - Parallel.map(initial_secret_numbers) { |initial_secret_number| |
11 |
| - 2000.times.inject(initial_secret_number) { |secret_number, _| |
12 |
| - secret_number = ((secret_number * 64) ^ secret_number) % 16777216 |
13 |
| - secret_number = ((secret_number / 32).floor ^ secret_number) % 16777216 |
14 |
| - secret_number = ((secret_number * 2048) ^ secret_number) % 16777216 |
15 |
| - } |
16 |
| - }.reduce(&:+) |
| 21 | + puts "starting part1" if ENV['DEBUG'] |
| 22 | + |
| 23 | + Parallel |
| 24 | + .map(buyers) { |buyer| buyer.secret_numbers.last } |
| 25 | + .reduce(&:+) |
17 | 26 | end
|
18 | 27 |
|
19 | 28 | # @example
|
20 |
| - # day.part2 #=> 'how are you' |
| 29 | + # day = Day22.new('1 2 3 2024') |
| 30 | + # day.part2 #=> 23 |
21 | 31 | def part2
|
| 32 | + puts "starting part2" if ENV['DEBUG'] |
| 33 | + |
| 34 | + unique_sequences = |
| 35 | + buyers |
| 36 | + .inject(Set.new) { |unique_sequences, buyer| |
| 37 | + unique_sequences.merge(buyer.price_sequence_table.keys) |
| 38 | + } |
| 39 | + |
| 40 | + puts "unique_sequences: #{unique_sequences.count}" if ENV['DEBUG'] |
| 41 | + |
| 42 | + Parallel |
| 43 | + .map(unique_sequences) { |sequence| |
| 44 | + buyers |
| 45 | + .inject(0) { |bananas, buyer| |
| 46 | + bananas += (buyer.price_sequence_table[sequence] || 0) |
| 47 | + } |
| 48 | + } |
| 49 | + .max |
22 | 50 | end
|
23 | 51 |
|
24 | 52 | EXAMPLE_INPUT = File.read("../inputs/day22-example-input.txt")
|
25 | 53 | end
|
| 54 | + |
| 55 | +class Buyer |
| 56 | + attr_reader :secret_numbers, :prices, :price_sequence_table |
| 57 | + |
| 58 | + # @example |
| 59 | + # buyer = Buyer.new(1) |
| 60 | + # buyer.secret_numbers.first #=> 1 |
| 61 | + # buyer.secret_numbers.last #=> 8685429 |
| 62 | + # buyer.prices.first #=> 1 |
| 63 | + # buyer.prices.last #=> 9 |
| 64 | + def initialize(initial_secret_number) |
| 65 | + @secret_numbers = [initial_secret_number] |
| 66 | + @prices = [initial_secret_number % 10] |
| 67 | + @price_sequence_table = Hash.new |
| 68 | + generate_secret_numbers |
| 69 | + generate_sequence_lookup |
| 70 | + end |
| 71 | + |
| 72 | + # @example 1 |
| 73 | + # buyer = Buyer.new(1) |
| 74 | + # buyer.secret_numbers.last #=> 8685429 |
| 75 | + # @example 10 |
| 76 | + # buyer = Buyer.new(10) |
| 77 | + # buyer.secret_numbers.last #=> 4700978 |
| 78 | + # @example 100 |
| 79 | + # buyer = Buyer.new(100) |
| 80 | + # buyer.secret_numbers.last #=> 15273692 |
| 81 | + # @example 2024 |
| 82 | + # buyer = Buyer.new(2024) |
| 83 | + # buyer.secret_numbers.last #=> 8667524 |
| 84 | + def generate_secret_numbers(evolutions=2000) |
| 85 | + evolutions.times do |
| 86 | + secret_number = @secret_numbers.last |
| 87 | + secret_number = ((secret_number * 64) ^ secret_number) % 16777216 |
| 88 | + secret_number = ((secret_number / 32).floor ^ secret_number) % 16777216 |
| 89 | + secret_number = ((secret_number * 2048) ^ secret_number) % 16777216 |
| 90 | + @secret_numbers << secret_number |
| 91 | + @prices << secret_number % 10 |
| 92 | + end |
| 93 | + end |
| 94 | + |
| 95 | + # @example |
| 96 | + # buyer = Buyer.new(123) |
| 97 | + # buyer.price_changes.take(9) #=> [-3, 6, -1, -1, 0, 2, -2, 0, -2] |
| 98 | + # buyer.price_changes.count #=> 2000 |
| 99 | + def price_changes |
| 100 | + @prices.each_cons(2).map { |a, b| b - a } |
| 101 | + end |
| 102 | + |
| 103 | + # @example |
| 104 | + # buyer = Buyer.new(1) |
| 105 | + # buyer.price_sequence_table[ [-2,1,-1,3] ] #=> 7 |
| 106 | + def generate_sequence_lookup |
| 107 | + price_changes.each_cons(4).with_index do |four_deltas, i| |
| 108 | + @price_sequence_table[four_deltas] ||= @prices[i + 4] |
| 109 | + end |
| 110 | + end |
| 111 | + |
| 112 | + def price_sequence_table |
| 113 | + generate_sequence_lookup if @price_sequence_table.empty? |
| 114 | + @price_sequence_table |
| 115 | + end |
| 116 | +end |
0 commit comments