Day 14
prelude
https://adventofcode.com/2024/day/14
import { inputDay, munge } from "./lib/utilities.js"
const test = munge(`p=0,4 v=3,-3
p=6,3 v=-1,-3
p=10,3 v=-1,2
p=2,0 v=2,-1
p=0,0 v=1,3
p=3,0 v=-2,-2
p=7,6 v=-1,-3
p=3,0 v=-1,-2
p=9,3 v=2,3
p=7,3 v=-1,2
p=2,4 v=2,-3
p=9,5 v=-3,-3
`)
display(test)
const input = await inputDay(14)
part 1
Some functions for operations on 2d points
function mul(v, n) {
return [v[0] * n, v[1] * n]
}
function mod(v1, v2) {
return [v1[0] % v2[0], v1[1] % v2[1]]
}
function add(v1, v2) {
return [v1[0] + v2[0], v1[1] + v2[1]]
}
// convert negative values into positive ones according to the rules. We end up
// with negative points because javascript's mod rules are that -3 mod 7 is -3;
// so convert it into (7-3) == 4
function pos(v1, v2) {
return [v1[0] < 0 ? v2[0] + v1[0] : v1[0], v1[1] < 0 ? v2[1] + v1[1] : v1[1]]
}
With those in place, we can write a function to spin a point n
seconds
function spin(p, v1, map, seconds) {
return pos(mod(add(p, mul(v1, seconds)), map), map)
}
And to find how many are in each quadrant
function quad(ps, map) {
const quads = [0, 0, 0, 0]
const xm = Math.floor(map[0] / 2)
const ym = Math.floor(map[1] / 2)
for (const [x, y] of ps) {
if (x < xm && y < ym) quads[0]++
if (x > xm && y < ym) quads[1]++
if (x > xm && y > ym) quads[2]++
if (x < xm && y > ym) quads[3]++
}
return quads.reduce((acc, x) => acc * x, 1)
}
Then we can print out our answer
let map = [11, 7]
display(
quad(
test.map(([, px, py, , vx, vy]) => spin([px, py], [vx, vy], map, 100)),
map,
),
)
map = [101, 103]
display(
quad(
input.map(([, px, py, , vx, vy]) => spin([px, py], [vx, vy], map, 100)),
map,
),
)
part 2
How to find an image in a sea of static? There's a lot of ways, but let's start by checking if the standard deviation of points at a particular index is lower than the others.
Let's define a function to calculate the standard deviation of a set of 2d points
function div(v1, n) {
return [v1[0] / n, v1[1] / n]
}
function dist(v1, v2) {
return Math.sqrt(Math.pow(v1[0] - v2[0], 2) + Math.pow(v1[1] - v2[1], 2))
}
function stdev(pts) {
// Get the mean of the set of points
const mean = div(
pts.reduce(([sx, sy], [x, y]) => [sx + x, sy + y], [0, 0]),
pts.length,
)
// calculate the sum of the squared distance of each point from the mean
const diff = pts.reduce((acc, pt) => acc + Math.pow(dist(pt, mean), 2), 0)
// return the square root of the sum
return Math.sqrt(diff / pts.length)
}
Then we can graph the standard deviation of the first 10,000 steps, and see that there's an outlier near step 8,000
const map = [101, 103]
const stdevs = []
for (let i = 0; i < 10_000; i++) {
const pts = input.map(([, px, py, , vx, vy]) =>
spin([px, py], [vx, vy], map, i),
)
stdevs.push(stdev(pts))
}
display(
Plot.plot({
marks: [
Plot.line(stdevs, {
stroke: "#4269d0",
x: Plot.indexOf,
y: Plot.identity,
}),
],
}),
)
Which one is that minimum?
function min(a) {
let min = Number.MAX_VALUE
let minIdx = -1
for (let i = 0; i < a.length; i++) {
if (a[i] < min) {
minIdx = i
min = a[i]
}
}
return [min, minIdx]
}
display(min(stdevs))
Let's take a look at step 8053: