Day 22
# puzzle prompt: https://adventofcode.com/2024/day/22
import sys
import time
sys.path.insert(0, "..")
from base.advent import *
class Solution(InputAsLinesSolution):
_year = 2024
_day = 22
_is_debugging = False
def CalcAndMixAndPrune(self, secret): # recipe from the puzzle
next_secret = secret
x = next_secret * 64
next_secret ^= x
next_secret %= 16777216
x = next_secret // 32
next_secret ^= x
next_secret %= 16777216
x = next_secret * 2048
next_secret ^= x
next_secret %= 16777216
return next_secret
def GetNthSecret(self, secret, n):
for _ in range(n):
secret = self.CalcAndMixAndPrune(secret)
return secret
def CreateSecrets(self, input):
res = sum(self.GetNthSecret(secret, 2000) for secret in input)
return res
def GetPrices(self, s, n):
prices = []
previous_price = s % 10
for _ in range(n):
s = self.CalcAndMixAndPrune(s)
price = s % 10
change = price - previous_price
prices.append((price, change))
previous_price = price
return prices
def GetSequences(self, s, n):
sequences = {}
prices = self.GetPrices(s, n)
for i in range(3, n):
trailing_4 = prices[i - 3 : i + 1]
sequence = tuple(price_change[1] for price_change in trailing_4)
if sequence in sequences:
continue
price = prices[i][0]
sequences[sequence] = price
return sequences
def BuyBananas(self, input):
all_sequences = set()
sequence_prices = []
for secret in input:
sequences = self.GetSequences(secret, 2000)
sequence_prices.append(sequences)
for k, _ in sequences.items():
all_sequences.add(k)
best_price = 0
for sequence in all_sequences:
total_price = sum(price.get(sequence, 0) for price in sequence_prices)
if total_price > best_price:
best_price = total_price
return best_price
def pt1(self, input):
self.debug(input)
res = self.CreateSecrets(input)
return res
def pt2(self, input):
self.debug(input)
res = self.BuyBananas(input)
return res
def part_1(self):
start_time = time.time()
res = self.pt1(self.input)
end_time = time.time()
self.solve("1", res, (end_time - start_time))
def part_2(self):
start_time = time.time()
res = self.pt2(self.input)
end_time = time.time()
self.solve("2", res, (end_time - start_time))
if __name__ == "__main__":
solution = Solution(to_int=True)
solution.part_1()
solution.part_2()