Skip to content

Commit

Permalink
Solution for 2024, Day 22
Browse files Browse the repository at this point in the history
  • Loading branch information
zodac committed Dec 25, 2024
1 parent fbb63c8 commit 813929a
Show file tree
Hide file tree
Showing 6 changed files with 285 additions and 2 deletions.
172 changes: 172 additions & 0 deletions 2024/src/main/java/me/zodac/advent/Day22.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
/*
* BSD Zero Clause License
*
* Copyright (c) 2021-2024 zodac.me
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
* IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

package me.zodac.advent;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

/**
* Solution for 2024, Day 22.
*
* @see <a href="https://adventofcode.com/2024/day/22">[2024: 22] Monkey Market</a>
*/
public final class Day22 {

private static final int STEP_1_MULTIPLICATION_CONSTANT = 6; // 2^6 = 64
private static final int STEP_3_MULTIPLICATION_CONSTANT = 11; // 2^11 = 2048
private static final int DIVISION_CONSTANT = 32;
private static final long PRUNE_CONSTANT = 16777216L;
private static final int SECRET_NUMBER_INDEX_TO_FIND = 2000;

// Radix values for part2 speed up
private static final int DIFF_RADIX = 19;
private static final int DIFF_RADIX_CUBED = DIFF_RADIX * DIFF_RADIX * DIFF_RADIX;
private static final int DIFF_RADIX_OFFSET = 9;

private Day22() {

}

/**
* Given a {@link Collection} of initial secret numbers, we must calculate the remaining secret numbers in the sequence. We then find the
* {@value #SECRET_NUMBER_INDEX_TO_FIND}th secret number, and sum them all.
*
* @param initialSecretNumbers the initial secret numbers to generate the remaining sequence
* @return the sum of {@value #SECRET_NUMBER_INDEX_TO_FIND}th secret numbers
*/
public static long calculateSumOf2000thSecretNumbers(final Collection<Integer> initialSecretNumbers) {
final Map<Integer, List<Long>> secretNumbersByInitial = generateSecretNumbers(initialSecretNumbers);

return secretNumbersByInitial
.values()
.stream()
.mapToLong(List::getLast)
.sum();
}

/**
* Given a {@link Collection} of initial secret numbers, we must calculate the remaining secret numbers in the sequence. We then find the
* {@value #SECRET_NUMBER_INDEX_TO_FIND}th secret number. For each secret number, we retrieve the 'price', which is the last digit of the number.
* We also get a running diff of the prices for the full sequence of secret numbers.
*
* <p>
* For each sequence of 4 running diffs, we want to find the sequence that returns the greatest value. The first price of the sequence is
* considered for each {@code initialSecretNumber}, and each value is added together. We should find the maximum number of the secret numbers
* possible, considering all possible 4-runs of diffs. This will be the maximum number of bananas to be requested.
*
* @param initialSecretNumbers the initial secret numbers to generate the remaining sequence
* @return the maximum number of bananas
*/
public static long calculateMaximumPossibleBananas(final Collection<Integer> initialSecretNumbers) {
final Map<Integer, List<Long>> secretNumbersByInitial = generateSecretNumbers(initialSecretNumbers);

final Map<Long, Long> numberOfBananasByDiff = new HashMap<>();
for (final Map.Entry<Integer, List<Long>> entry : secretNumbersByInitial.entrySet()) {
final List<Long> secretNumbers = entry.getValue();
final List<Long> prices = secretNumbers.stream().map(l -> l % 10L).toList();

final Collection<Long> existingDiffs = new HashSet<>();

long runningDiff = 0L;
for (int i = 1; i < prices.size(); i++) {
// Diff calculated based on following logic, gives a concise value for the diff that can be used for comparisons
// https://github.com/p-kovacs/advent-of-code-2024/blob/master/src/main/java/com/github/pkovacs/aoc/y2024/Day22.java#L33
runningDiff = (runningDiff % DIFF_RADIX_CUBED) * DIFF_RADIX + (prices.get(i) - prices.get(i - 1) + DIFF_RADIX_OFFSET);

// We don't want to include the diff until there are at least 4 values, but removing the check still gives the correct result
if (existingDiffs.add(runningDiff)) {
numberOfBananasByDiff.merge(runningDiff, prices.get(i), Long::sum);
}
}
}

return numberOfBananasByDiff
.values()
.stream()
.mapToLong(Long::longValue)
.max()
.orElse(0L);
}

private static Map<Integer, List<Long>> generateSecretNumbers(final Collection<Integer> initialSecretNumbers) {
final Map<Integer, List<Long>> secretNumbersByInitial = new HashMap<>();
for (final int initialSecretNumber : initialSecretNumbers) {
final List<Long> secretNumbers = calculateFinalSecretNumber(initialSecretNumber);
secretNumbersByInitial.put(initialSecretNumber, secretNumbers);
}
return secretNumbersByInitial;
}

private static List<Long> calculateFinalSecretNumber(final long initialSecretNumber) {
final List<Long> secretNumbers = new ArrayList<>();
secretNumbers.add(initialSecretNumber);

long secretNumber = initialSecretNumber;
for (int i = 0; i < SECRET_NUMBER_INDEX_TO_FIND; i++) {
secretNumber = generateNextSecretNumber(secretNumber);
secretNumbers.add(secretNumber);
}

return secretNumbers;
}

private static long generateNextSecretNumber(final long secretNumber) {
final long step1Result = step1(secretNumber);
final long step2Result = step2(step1Result);
return step3(step2Result);
}

private static long step1(final long secretNumber) {
final long multiplicationResult = mul(secretNumber, true);
final long mixedResult = mix(multiplicationResult, secretNumber);
return prune(mixedResult);
}

private static long step2(final long secretNumber) {
final long divisionResult = div(secretNumber);
final long mixedResult = mix(divisionResult, secretNumber);
return prune(mixedResult);
}

private static long step3(final long secretNumber) {
final long multiplicationResult = mul(secretNumber, false);
final long mixedResult = mix(multiplicationResult, secretNumber);
return prune(mixedResult);
}

private static long mul(final long input, final boolean isStep1) {
return input << (isStep1 ? STEP_1_MULTIPLICATION_CONSTANT : STEP_3_MULTIPLICATION_CONSTANT);
}

private static long div(final long input) {
return Math.floorDiv(input, DIVISION_CONSTANT);
}

private static long mix(final long first, final long second) {
return first ^ second;
}

private static long prune(final long input) {
return input % PRUNE_CONSTANT;
}

}
78 changes: 78 additions & 0 deletions 2024/src/test/java/me/zodac/advent/Day22Test.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* BSD Zero Clause License
*
* Copyright (c) 2021-2024 zodac.me
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
* IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

package me.zodac.advent;

import static org.assertj.core.api.Assertions.assertThat;

import java.util.List;
import me.zodac.advent.input.InputReader;
import org.junit.jupiter.api.Test;

/**
* Tests to verify answers for {@link Day22}.
*/
class Day22Test {

private static final String INPUT_FILENAME = "day22.txt";
private static final String INPUT_FILENAME_PART_2 = "day22_2.txt";

@Test
void example() {
final List<Integer> valuesPart1 = InputReader
.forExample(INPUT_FILENAME)
.asIntegers()
.readAllLines();

final long part1Result = Day22.calculateSumOf2000thSecretNumbers(valuesPart1);
assertThat(part1Result)
.isEqualTo(37_327_623L);

final List<Integer> valuesPart2 = InputReader
.forExample(INPUT_FILENAME_PART_2)
.asIntegers()
.readAllLines();

final long part2Result = Day22.calculateMaximumPossibleBananas(valuesPart2);
assertThat(part2Result)
.isEqualTo(23L);
}

@Test
void part1() {
final List<Integer> values = InputReader
.forPuzzle(INPUT_FILENAME)
.asIntegers()
.readAllLines();

final long part1Result = Day22.calculateSumOf2000thSecretNumbers(values);
assertThat(part1Result)
.isEqualTo(14_119_253_575L);
}

@Test
void part2() {
final List<Integer> values = InputReader
.forPuzzle(INPUT_FILENAME)
.asIntegers()
.readAllLines();

final long part2Result = Day22.calculateMaximumPossibleBananas(values);
assertThat(part2Result)
.isEqualTo(1_600L);
}
}
4 changes: 4 additions & 0 deletions 2024/src/test/resources/day22.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
1
10
100
2024
4 changes: 4 additions & 0 deletions 2024/src/test/resources/day22_2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
1
2
3
2024
27 changes: 26 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Advent Of Code: Java Edition

![2024](https://img.shields.io/badge/2024%20⭐-36-yellow)
![2024](https://img.shields.io/badge/2024%20⭐-38-yellow)
![2023](https://img.shields.io/badge/2023%20⭐-32-yellow)
![2022](https://img.shields.io/badge/2022%20⭐-28-yellow)
![2021](https://img.shields.io/badge/2021%20⭐-19-orange)
Expand Down Expand Up @@ -78,6 +78,11 @@ The source code is released under the [BSD Zero Clause License](https://opensour
| [Day 18](https://adventofcode.com/2024/day/18) | 22,587 ⭐ | 21,997 ⭐ |
| [Day 19](https://adventofcode.com/2024/day/19) | 7,778 ⭐ | 6,372 ⭐ |
| [Day 20](https://adventofcode.com/2024/day/20) | 1,890 ⭐ | 11,505 ⭐ |
| [Day 21](https://adventofcode.com/2024/day/21) | 21,025 ⭐ | 19,375 ⭐ |
| [Day 22](https://adventofcode.com/2024/day/22) | | |
| [Day 23](https://adventofcode.com/2024/day/23) | | |
| [Day 24](https://adventofcode.com/2024/day/24) | | |
| [Day 25](https://adventofcode.com/2024/day/25) | | |

</details>

Expand All @@ -102,6 +107,15 @@ The source code is released under the [BSD Zero Clause License](https://opensour
| [Day 14](https://adventofcode.com/2023/day/14) | 2,890 ⭐ | 5,790 ⭐ |
| [Day 15](https://adventofcode.com/2023/day/15) | 3,486 ⭐ | 3,046 ⭐ |
| [Day 16](https://adventofcode.com/2023/day/16) | 2,697 ⭐ | 2,557 ⭐ |
| [Day 17](https://adventofcode.com/2023/day/17) | | |
| [Day 18](https://adventofcode.com/2023/day/18) | | |
| [Day 19](https://adventofcode.com/2023/day/19) | | |
| [Day 20](https://adventofcode.com/2023/day/20) | | |
| [Day 21](https://adventofcode.com/2023/day/21) | | |
| [Day 22](https://adventofcode.com/2023/day/22) | | |
| [Day 23](https://adventofcode.com/2023/day/23) | | |
| [Day 24](https://adventofcode.com/2023/day/24) | | |
| [Day 25](https://adventofcode.com/2023/day/25) | | |

</details>

Expand All @@ -124,6 +138,17 @@ The source code is released under the [BSD Zero Clause License](https://opensour
| [Day 12](https://adventofcode.com/2022/day/12) | 7,311 ⭐ | 6,766 ⭐ |
| [Day 13](https://adventofcode.com/2022/day/13) | 14,960 ⭐ | 14,025 ⭐ |
| [Day 14](https://adventofcode.com/2022/day/14) | 30,919 ⭐ | 30,485 ⭐ |
| [Day 17](https://adventofcode.com/2022/day/15) | | |
| [Day 17](https://adventofcode.com/2022/day/16) | | |
| [Day 17](https://adventofcode.com/2022/day/17) | | |
| [Day 18](https://adventofcode.com/2022/day/18) | | |
| [Day 19](https://adventofcode.com/2022/day/19) | | |
| [Day 20](https://adventofcode.com/2022/day/20) | | |
| [Day 21](https://adventofcode.com/2022/day/21) | | |
| [Day 22](https://adventofcode.com/2022/day/22) | | |
| [Day 23](https://adventofcode.com/2022/day/23) | | |
| [Day 24](https://adventofcode.com/2022/day/24) | | |
| [Day 25](https://adventofcode.com/2022/day/25) | | |

</details>

Expand Down
2 changes: 1 addition & 1 deletion advent-of-code-inputs

0 comments on commit 813929a

Please sign in to comment.