AoC 2019
Day 1: The Tyranny of the Rocket Equation
let fs = require('fs');
let input = fs.readFileSync('input.txt', 'utf8');
let f = x => Math.floor(x / 3) - 2;
let go = x => f(x) <= 0 ? 0 : f(x) + go(f(x));
let run = func => input.split('\n').map(x => func(x)).reduce((a, b) => a + b);
console.log(`Part 1: ${run(f)}\nPart 2: ${run(go)}`);
// Part 1: 3376997
// Part 2: 5062623
Day 2: 1202 Program Alarm
let fs = require('fs');
let input = fs.readFileSync('input.txt', 'utf-8').split(',').map(x => parseInt(x));
let gravityAssist = (noun, verb, ...A) => {
A[1] = noun, A[2] = verb;
for (let i = 0; i < A.length; i += 4) {
let [op, u, v, w] = [A[i], A[i + 1], A[i + 2], A[i + 3]];
if (op == 99)
break;
if (op == 1) A[w] = A[u] + A[v];
if (op == 2) A[w] = A[u] * A[v];
}
return A[0];
};
console.log(`Part 1: ${gravityAssist(12, 2, ...input)}`);
for (let i = 0; i < 100; ++i)
for (let j = 0; j < 100; ++j)
if (gravityAssist(i, j, ...input) == 19690720)
console.log(`Part 2: ${i}${j}`);
// Part 1: 5098658
// Part 2: 5064
Day 3: Crossed Wires
let fs = require('fs');
let input = fs.readFileSync('input.txt', 'utf-8');
class Wire {
constructor(paths) {
this.seen = new Set();
this.total = 0;
this.steps = new Map();
this.pos = {
row: 0,
col: 0
};
this.traverse(paths);
}
traverse(paths) {
for (let path of paths)
this.walk(path);
}
walk(path) {
let [dir, steps] = [path[0], Number(path.slice(1))];
while (steps--) {
++this.total;
if (dir == 'U') --this.pos.row; if (dir == 'D') ++this.pos.row;
if (dir == 'L') --this.pos.col; if (dir == 'R') ++this.pos.col;
let key = `${this.pos.row},${this.pos.col}`;
this.seen.add(key);
if (this.steps.get(key) && this.steps.get(key) <= this.total)
continue; // only add keys which don't exist; only update keys with larger total
this.steps.set(key, this.total);
}
}
}
let [A, B] = input.split('\n')
.map(line => line.split(','))
.map(paths => new Wire(paths));
let intersect = [...A.seen].filter(x => B.seen.has(x));
let closest = [...intersect]
.map((key) => key.split(',').map(Number))
.map(([i, j]) => Math.abs(i) + Math.abs(j))
.sort((a, b) => a - b);
let minDelay = [...intersect]
.map((key) => A.steps.get(key) + B.steps.get(key))
.sort((a, b) => a - b);
console.log(`Part 1: ${closest[0]}\nPart 2: ${minDelay[0]}`);
// Part 1: 731
// Part 2: 5672
Day 4: Secure Container
let ok1 = x => {
let A = x.toString().split('').map(Number);
return A.every((x, i) => i == 0 || A[i - 1] <= x) &&
A.some((x, i) => i > 0 && A[i - 1] == x);
}
let ok2 = x => {
let A = x.toString().split('').map(Number);
let cnt = new Map();
for (let val of A)
cnt.set(val, 1 + (cnt.get(val) || 0));
for (let [key, val] of cnt)
if (val == 2)
return true;
return false;
}
let part1 = 0, part2 = 0;
for (let x = 197487; x < 673251; ++x) {
if (ok1(x))
++part1;
if (ok1(x) && ok2(x))
++part2;
}
console.log(`Part 1: ${part1}\nPart 2: ${part2}`);
// Part 1: 1640
// Part 2: 1126
Day 5: Sunny with a Chance of Asteroids
let fs = require('fs')
let input = fs.readFileSync('input.txt', 'utf-8').split(',').map(Number);
let run = (id, A, ans = 0) => {
let pad = cmd => ('00000' + cmd).substring(('00000' + cmd).length - 5);
let op = 0, instructions = [0, 4, 4, 2, 2, 0, 0, 4, 4];
for (let i = 0; op != 99; i += instructions[op]) {
let cmd = pad(A[i]);
let [u, v, w] = [A[i + 1], A[i + 2], A[i + 3]];
u = cmd[2] == 0 ? A[u] : u;
v = cmd[1] == 0 ? A[v] : v;
op = parseInt(cmd.substring(cmd.length - 2));
if (op == 1) A[w] = u + v;
if (op == 2) A[w] = u * v;
if (op == 3) w = A[i + 1], A[w] = id;
if (op == 4) ans = u;
if (op == 5) i = (u != 0) ? v : i + 3;
if (op == 6) i = (u == 0) ? v : i + 3;
if (op == 7) A[w] = u < v;
if (op == 8) A[w] = u == v;
}
return ans;
};
console.log(`Part 1: ${run(1, [...input])}\nPart 2: ${run(5, [...input])}`);
// Part 1: 16574641
// Part 2: 15163975
Day 6: Universal Orbit Map
let fs = require('fs');
let input = fs.readFileSync('input.txt', 'utf-8').split('\n').map(edge => edge.split(')'));
let edges = new Map(); // lookup parent u by child v: [v, u] (ie. u -> v)
for (let [u, v] of input) edges.set(v, u); // child v has parent u (ie. u -> v)
let go = (edges, v) => !edges.get(v) ? 0 : 1 + go(edges, edges.get(v)); // parent u = edges.get(child v)
let total = [...edges.keys()].map(v => go(edges, v)).reduce((a, b) => a + b);
console.log(`Part 1: ${total}`);
let [you, san] = [edges.get('YOU'), edges.get('SAN')];
let path = { you: [], san: [] };
while (you) path.you.unshift(you), you = edges.get(you); // push you's parents to front of path you
while (san) path.san.unshift(san), san = edges.get(san); // push san's parents to front of path san
let ancestor = null; // traverse paths from root till divergence to find the first common ancestor
for (let i = 0; ; ++i) {
if (path.you[i] != path.san[i]) {
ancestor = path.you[i - 1]; // same as path.san[i-1] (ie. you and san share this first common ancestor)
break;
}
}
let steps = { you: 0, san: 0 };
[you, san] = [edges.get('YOU'), edges.get('SAN')];
while (you != ancestor) ++steps.you, you = edges.get(you);
while (san != ancestor) ++steps.san, san = edges.get(san);
console.log(`Part 2: ${steps.you + steps.san}`);
// Part 1: 453028
// Part 2: 562
Day 7: Amplification Circuit
- intcode_computer_day_07.js (slightly modified Day 5: Sunny with a Chance of Asteroids)
let fs = require('fs');
let A = fs.readFileSync('input.txt', 'utf-8').split(',').map(Number);
let run = require('../00_common/intcode_computer');
let permutations = A => {
if (A.length == 1)
return A;
return A.reduce((res, x, i, A, B = [...A]) => {
B.splice(i, 1); // B is A without A[i] (ie. x)
return res.concat(permutations(B).map(a => [].concat(x, a))); // recursively insert x into all other positions
}, []);
}
let maxThrust = (A, perms, loopback = false, max = -Infinity) => {
for (let perm of perms) {
let output = 0, pc = [];
let amp = [...Array(5)].map(row => [...A]);
perm.forEach((phase, i) => {
let j = 0;
[output, j] = run(amp[i], [phase, output]);
pc.push(j);
max = Math.max(max, output);
});
if (!loopback)
continue;
for (let i = 0; output > -Infinity; i = (i + 1) % 5) {
[output, pc[i]] = run(amp[i], [output], pc[i]);
max = Math.max(max, output);
}
}
return max;
};
let perm1 = permutations([0, 1, 2, 3, 4]),
perm2 = permutations([5, 6, 7, 8, 9]);
console.log(`Part 1: ${maxThrust(A, perm1)}\nPart 2: ${maxThrust(A, perm2, true)}`);
// Part 1: 117312
// Part 2: 1336480
Day 8: Space Image Format
let fs = require('fs');
let input = fs.readFileSync('input.txt', 'utf-8').split('');
let M = 6, N = 25, K = M * N, L = input.length / K;
let A = []; while (input.length) A.push(input.splice(0, N));
let T = [...Array(L)].map(row => Array(3).fill(0)); // tally of 0, 1, 2
A.forEach((row, i) => {
let k = Math.floor(i / M); // k-th layer
T[k][0] += row.filter(x => x == 0).length,
T[k][1] += row.filter(x => x == 1).length,
T[k][2] += row.filter(x => x == 2).length;
});
let cnt = T[0][0], min = 0;
for (let i = 1; i < L; ++i)
if (cnt > T[i][0])
cnt = T[i][0], min = i;
console.log(`Part 1: ${T[min][1] * T[min][2]}`);
for (let i = 0; i + M < M * L; ++i)
for (let j = 0; j < N; ++j)
if (A[i][j] != 2) // naive dp algo (ie. each prev layer is M rows away, only propagate non-transparent)
A[i + M][j] = A[i][j];
let msg = [];
for (let i = (M * L) - M; i < M * L; ++i) // last layer contains the dp algo result (ie. all layers coalesced)
msg.push(A[i].join('').replace(/0/g, ' '));
console.log('Part 2: ');
msg.forEach(row => console.log(row.replace(/1/g, '#')));
// Part 1: 1950
// Part 2:
// #### # # ## # # #
// # # # # # # # #
// ### ## # # #### #
// # # # #### # # #
// # # # # # # # #
// # # # # # # # ####
Day 9: Sensor Boost
- intcode_computer_day_09.js (slightly modified Day 5: Sunny with a Chance of Asteroids)
let run = (A, input, pc = 0) => {
let pad = cmd => ('00000' + cmd).substring(('00000' + cmd).length - 5);
let param = (A, mode, x, rel, write = false) => {
if (write)
return mode == 0 ? x : x + rel;
if (mode == 0) return A[x];
if (mode == 1) return x;
if (mode == 2) return A[x + rel];
};
let iter = input[Symbol.iterator]();
let op = 0, instructions = [0, 4, 4, 2, 2, 0, 0, 4, 4, 2];
let rel = 0; // relative base
for (let i = pc; op != 99; i += instructions[op]) {
let cmd = pad(A[i]);
op = Number(cmd.substring(cmd.length - 2));
let [u, v, w] = [A[i + 1], A[i + 2], A[i + 3]];
let mode = { u: Number(cmd[2]), v: Number(cmd[1]), w: Number(cmd[0]) };
u = param(A, mode.u, u, rel, op == 3);
v = param(A, mode.v, v, rel);
w = param(A, mode.w, w, rel, true);
if (op == 1) A[w] = u + v;
if (op == 2) A[w] = u * v;
if (op == 3) A[u] = iter.next().value;
if (op == 4) return u;
if (op == 5) i = (u != 0) ? v : i + 3;
if (op == 6) i = (u == 0) ? v : i + 3;
if (op == 7) A[w] = u < v ? 1 : 0;
if (op == 8) A[w] = u == v ? 1 : 0;
if (op == 9) rel += u;
}
};
module.exports = run;
- main.js: invoke run()…
let fs = require('fs');
let A = fs.readFileSync('input.txt', 'utf-8').split(',').map(Number);
let run = require('../00_common/intcode_computer_day_09');
console.log(`Part 1: ${run(A, [1])}\nPart 2: ${run(A, [2])}`);
// Part 1: 3460311188
// Part 2: 42202
Day 10: Monitoring Station
- Note: I let
x
denote the row andy
denote the column, this is opposite per the problem statement (ie.y
is the row andx
is the column). However, this is irrelevant since I’m consistent with this representation.
let fs = require('fs');
let input = fs.readFileSync('input.txt', 'utf-8').split('\n').map(row => row.split(''));
let detect = A => {
let gcd = (a, b) => b == 0 ? Math.abs(a) : gcd(b, a % b);
let delta = (a, b) => {
let diff = {}; [diff.x, diff.y] = [b.x - a.x, b.y - a.y];
let gdiv = gcd(diff.x, diff.y);
return { x: Math.floor(diff.x / gdiv), y: Math.floor(diff.y / gdiv) };
};
let [M, N] = [A.length, A[0].length];
let roids = []; for (let i = 0; i < M; ++i) for (let j = 0; j < N; ++j) if (A[i][j] == '#') roids.push([i, j]);
let K = roids.length;
let key = roid => `${roid[0]},${roid[1]}`;
let seen = new Set([...roids].map(roid => key(roid)));
let all = [];
for (let u = 0; u < K; ++u) {
let a = {}; [a.x, a.y] = [...roids[u]];
let cand = { x: a.x, y: a.y, seen: new Set() }; // a candidate for best asteroid
for (let v = 0; v < K; ++v) {
if (u == v) continue; // do not compare asteroids to themselves
let b = {}; [b.x, b.y] = [...roids[v]];
let d = delta(a, b); // take one step (d.x, d.y) at a time from (a -> b) to find (c)
let c = { x: a.x + d.x, y: a.y + d.y, isBlocking: false };
while (!(c.x == b.x && c.y == b.y)) {
if (seen.has(key([c.x, c.y])))
c.isBlocking = true;
c.x += d.x, c.y += d.y;
}
if (!c.isBlocking)
cand.seen.add([b.x, b.y]);
}
all.push(cand);
}
return all.sort((a, b) => b.seen.size - a.seen.size)[0];
}
let best = detect(input);
console.log(`Part 1: ${best.seen.size}`);
// Part 1: 247
Day 11: Space Police
class Robot {
constructor(start = 0) {
this.m = new Map();
this.i = 0;
this.j = 0;
this.d = 0;
this.D = [0, 1, 2, 3]; // clockwise dirs 0 = up, 1 = right, 2 = down, 3 = left
this.L = [3, 0, 1, 2]; // left-turns (prev dir is index, next dir is value at that index)
this.R = [1, 2, 3, 0]; // right-turns (prev dir is index, next dir is value at that index)
this.painted = new Set();
this.white = new Set();
this.paint(start)
}
color() {
let [i, j] = [this.i, this.j];
if (!this.m.has(i))
this.m.set(i, new Map());
let row = this.m.get(i);
if (!row.has(j))
row.set(j, 0); // default value 0 for non-existent entries
return row.get(j);
}
paint(v) {
let [i, j] = [this.i, this.j];
if (!this.m.has(i))
this.m.set(i, new Map());
let row = this.m.get(i);
row.set(j, v);
let key = `${i},${j}`;
this.painted.add(key);
if (this.color() == 1)
this.white.add(key);
}
step() {
let d = this.d;
if (d == 0) --this.i; if (d == 2) ++this.i;
if (d == 3) --this.j; if (d == 1) ++this.j;
}
turn(dir) {
if (dir == 0) this.d = this.L[this.d];
if (dir == 1) this.d = this.R[this.d];
}
}
module.exports = Robot;
const Robot = require('../00_common/Robot');
let run = (A, robot = new Robot(), pc = 0) => {
let pad = cmd => ('00000' + cmd).substring(('00000' + cmd).length - 5);
let param = (A, mode, x, rel, write = false) => {
if (write)
return mode == 0 ? x : x + rel;
if (mode == 0) return A[x];
if (mode == 1) return x;
if (mode == 2) return A[x + rel];
};
let op = 0, instructions = [0, 4, 4, 2, 2, 0, 0, 4, 4, 2];
let rel = 0; // relative base
let q = []; // command queue
for (let i = pc; op != 99; i += instructions[op]) {
let cmd = pad(A[i]);
op = Number(cmd.substring(cmd.length - 2));
let [u, v, w] = [A[i + 1], A[i + 2], A[i + 3]];
let mode = { u: Number(cmd[2]), v: Number(cmd[1]), w: Number(cmd[0]) };
u = param(A, mode.u, u, rel, op == 3);
v = param(A, mode.v, v, rel);
w = param(A, mode.w, w, rel, true);
if (op == 1) A[w] = u + v;
if (op == 2) A[w] = u * v;
if (op == 3) A[u] = robot.color();
if (op == 4) q.push(u);
if (op == 5) i = (u != 0) ? v : i + 3;
if (op == 6) i = (u == 0) ? v : i + 3;
if (op == 7) A[w] = u < v ? 1 : 0;
if (op == 8) A[w] = u == v ? 1 : 0;
if (op == 9) rel += u;
if (q.length == 2) { // process cmd q: [color, dir]
let [color, dir] = q;
robot.paint(color);
robot.turn(dir);
robot.step();
q.splice(0, q.length); // empty cmd q
}
}
};
module.exports = run;
const Robot = require('../00_common/Robot');
let fs = require('fs');
let A = fs.readFileSync('input.txt', 'utf-8').split(',').map(Number);
let run = require('../00_common/intcode_computer_day_11');
let [r1, r2] = [new Robot(), new Robot(1)];
run(A, r1), run(A, r2);
let [M, N] = [6, 43]; // I printed the min/max i,j to know these magic numbers
let out = [...Array(M)].map(row => new Array(N).fill(' '));
for (let key of r2.white) {
let [i, j] = key.split(',').map(Number);
out[i][j] = '#';
}
console.log(`Part 1: ${r1.painted.size}\nPart 2:`);
for (let i = 0; i < 6; ++i) {
let row = [];
for (let j = 0; j < 43; ++j)
row.push(out[i][j]);
console.log([...row].join(''));
}
// Part 1: 1967
// Part 2:
// ## # ### # # #### ## #### ### # #
// # # # # # # # # # # # # # #
// ## ### # # ### # # ### ##
// # # # # # # # # ## # # # # #
// # # # # # # # # # # # # # #
// # # ### ## #### ### #### ### # #
Day 12: The N-Body Problem
let fs = require('fs');
let M = 4, N = 3; // 4 moons each with 3 positions (x, y, z)
let P = fs.readFileSync('input.txt', 'utf-8').split('\n')
.map(line => line.substring(0, line.length - 1).substring(1))
.map(line => line.split(','))
.map(A => A.map(s => Number(s.trim().substring(2))));
let V = [...Array(4)].map(row => new Array(3).fill(0));
let gravity = (P, V) => {
for (let u = 0; u < M; ++u)
for (let v = 0; v < M; ++v)
for (let j = 0; u != v && j < N; ++j) {
if (P[u][j] < P[v][j]) ++V[u][j];
if (P[u][j] > P[v][j]) --V[u][j];
}
}
let velocity = (P, V) => {
for (let i = 0; i < M; ++i)
for (let j = 0; j < N; ++j)
P[i][j] += V[i][j];
}
let k = 1000; // k-steps
while (k--)
gravity(P, V),
velocity(P, V);
let sum = A => A.reduce((a, b) => Math.abs(a) + Math.abs(b));
let energy = (P, V) => sum(P) * sum(V);
let total = 0;
for (let i = 0; i < M; ++i)
total += energy(P[i], V[i]);
console.log(`Part 1: ${total}`);
// Part 1: 8742
Day 13: Care Package
- Robot.js: Slightly modified Robot class from day 11. Below are additions for day 13.
class Robot {
constructor(start = 0) {
...
this.blocks = new Set();
}
paint(v) {
...
if (this.color() == 2) this.blocks.add(key); else this.blocks.delete(key);
}
goto(i, j) {
this.i = i, this.j = j;
}
}
const Robot = require('../00_common/Robot');
let run = (A, robot = new Robot(), pc = 0) => {
let pad = cmd => ('00000' + cmd).substring(('00000' + cmd).length - 5);
let param = (A, mode, x, rel, write = false) => {
if (write)
return mode == 0 ? x : x + rel;
if (mode == 0) return A[x];
if (mode == 1) return x;
if (mode == 2) return A[x + rel];
};
let op = 0, instructions = [0, 4, 4, 2, 2, 0, 0, 4, 4, 2];
let rel = 0; // relative base
let q = []; // command queue
for (let i = pc; op != 99; i += instructions[op]) {
let cmd = pad(A[i]);
op = Number(cmd.substring(cmd.length - 2));
let [u, v, w] = [A[i + 1], A[i + 2], A[i + 3]];
let mode = { u: Number(cmd[2]), v: Number(cmd[1]), w: Number(cmd[0]) };
u = param(A, mode.u, u, rel, op == 3);
v = param(A, mode.v, v, rel);
w = param(A, mode.w, w, rel, true);
if (op == 1) A[w] = u + v;
if (op == 2) A[w] = u * v;
if (op == 3) A[u] = robot.color();
if (op == 4) q.push(u);
if (op == 5) i = (u != 0) ? v : i + 3;
if (op == 6) i = (u == 0) ? v : i + 3;
if (op == 7) A[w] = u < v ? 1 : 0;
if (op == 8) A[w] = u == v ? 1 : 0;
if (op == 9) rel += u;
if (q.length == 3) { // process cmd q
let [col, row, tile] = q;
robot.goto(row, col);
robot.paint(tile);
q.splice(0, q.length); // empty cmd q
}
}
};
module.exports = run;
let fs = require('fs');
let A = fs.readFileSync('input.txt', 'utf-8').split(',').map(Number);
let Robot = require('../00_common/Robot');
let run = require('../00_common/intcode_computer_day_13');
let r1 = new Robot();
run([...A], r1);
console.log(`Part 1: ${r1.blocks.size}`);
// Part 1: 260