Day 11

prelude

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

import { inputDay, maybeNumber, range } from "./lib/utilities.js"
const parser = inp => inp.split(" ").map(maybeNumber)
const test = parser(`125 17`)
display(test)

const input = await inputDay(11, { parser })

part 1

I think I can guess where part 2 is going, but let's play along.

Start with a function to process a single stone

function t(stone) {
  if (stone == 0) return 1
  const ss = String(stone)
  if (ss.length % 2 === 0) {
    return [+ss.slice(0, ss.length / 2), +ss.slice(ss.length / 2, ss.length)]
  }
  return stone * 2024
}

We're going to have lots of repetitive results, so memoize them

/** memoize a function of a single comparable argument */
function memoize(f) {
  const memo = {}
  return n => (Object.hasOwn(memo, n) ? memo[n] : (memo[n] = f(n)))
}

Then iterate n times and return the result

function totalSize(stones, n) {
  const stoneVal = memoize(t)
  for (let i = 0; i < n; i++) {
    stones = stones.flatMap(stoneVal)
  }
  return stones.length
}

display(totalSize(test, 6))
display(totalSize(input, 25))

part 2

If we try to execute part 1's answer for part 2, it takes forever and the array grows so large that the program fails.

The key to solving it is realizing that we the array will be absolutely chock-full of duplicates, and all we care about is how many of any given number we have.

function totalSize2(stones, n) {
  const stoneVal = memoize(t)
  // An object with [stone: count] entries to track how many of each stone we
  // have
  let counts = Object.fromEntries(
    stones.map(s => [s, stones.filter(x => x == s).length]),
  )
  for (let i = 0; i < n; i++) {
    // an updated count list
    const c2 = {}
    Object.entries(counts).forEach(([stone, count]) => {
      const res = stoneVal(stone)
      if (Array.isArray(res)) {
        c2[res[0]] = count + (c2[res[0]] || 0)
        c2[res[1]] = count + (c2[res[1]] || 0)
      } else {
        c2[res] = count + (c2[res] || 0)
      }
    })
    // replace the count list
    counts = c2
  }

  // return the sum of the values of the counts list
  return Object.values(counts).reduce((a, b) => a + b)
}
display(totalSize2(test, 6))
display(totalSize2(input, 25))
display(totalSize2(input, 75))