Nime

Advent of Code 2024 Day 11

Day 11: Plutonian Pebbles

https://adventofcode.com/2024/day/11

Another day, another familiar location.

You are in a spot with a bunch of numbered stones in a straight line.

An example input looks like this:

input.txt
0 1 10 99 999

The input is one line. Each stone number is separated by a space.

Each time you blink, the stones change all at once according to a few rules.

Ooh, I know this one, it’s a cellular automaton, like the game of life

The rules for how the (markings on the) stones change:

  • 0 becomes 1
  • Even number of digit stones turn into 2 new stones:
    1. Left stone gets the first half of the digits
    2. Right stone gets the second half of the digits
  • Otherwise, the number get multiplied by 2024

Parsing

Because each stone is completely independent, and stones with the same number acts in exactly the same way, I chose to work on a map of key-value pairs where a key is a stone number, and the value is the amount of stones with that number.

Oh, and this is a day again where the numbers can get large.

fn parse(input: &str) -> HashMap<u64, u64> {
let mut stones: HashMap<u64, u64> = HashMap::new();
for num in input.split_ascii_whitespace() {
let num = num.parse().unwrap();
*stones.entry(num).or_default() += 1;
}
stones
}

Helper

Implementing the rules for what happens when you blink. This helper function takes in a map of stones, and returns a new map of stones.

fn blink(stones: &HashMap<u64, u64>) -> HashMap<u64, u64> {
let mut new = HashMap::new();
for (stone, amount) in stones {
if *stone == 0 {
*new.entry(1).or_default() += amount;
} else {
let digits = stone.ilog10() + 1;
if digits % 2 == 0 {
let magnitude = 10u64.pow(digits / 2);
*new.entry(stone % magnitude).or_default() += amount;
*new.entry(stone / magnitude).or_default() += amount;
} else {
*new.entry(stone * 2024).or_default() += amount;
}
}
}
new
}

Part 1

The question asks how many stones there would be after blinking 25 times.

day_11.rs
fn part_1(input: &str) -> u64 {
let mut stones = parse(input);
for _ in 0..25 {
stones = blink(&stones);
}
stones.values().sum()
}

Part 2

What was this, blinking for ants?!, it needs to be at least 3 times bigger!

The question asks how many stones there would be after blinking 75 times.

  1. Take the part 1 code
  2. Change 25 to 75
  3. Success
day_11.rs
fn part_2(input: &str) -> u64 {
let mut stones = parse(input);
for _ in 0..75 {
stones = blink(&stones);
}
stones.values().sum()
}

Final code

To combine both parts: Looping 75 times and recording the sum at loop number 25.

day_11.rs
use std::collections::HashMap;
fn parse(input: &str) -> HashMap<u64, u64> {
let mut stones: HashMap<u64, u64> = HashMap::new();
for num in input.split_ascii_whitespace() {
let num = num.parse().unwrap();
*stones.entry(num).or_default() += 1;
}
stones
}
fn blink(stones: &HashMap<u64, u64>) -> HashMap<u64, u64> {
let mut new = HashMap::new();
for (stone, amount) in stones {
if *stone == 0 {
*new.entry(1).or_default() += amount;
} else {
let digits = stone.ilog10() + 1;
if digits % 2 == 0 {
let magnitude = 10u64.pow(digits / 2);
*new.entry(stone % magnitude).or_default() += amount;
*new.entry(stone / magnitude).or_default() += amount;
} else {
*new.entry(stone * 2024).or_default() += amount;
}
}
}
new
}
fn both(input: &str) -> (u64, u64) {
let mut stones = parse(input);
let mut p1 = 0;
for i in 0..75 {
if i == 25 {
p1 = stones.values().sum();
}
stones = blink(&stones);
}
(p1, stones.values().sum())
}