// -*- node.jz -*- /** * Day 22, Part 1 & 2 * * Usage: * input >> stdin >> node day22.js $PART $ITERATIONS * e.g. * `cat input.txt | node day22.js 2 10000000` */ let input = '' process.stdin.on('readable', () => input += process.stdin.read() || '') process.stdin.on('end', () => main()) // Enums const NS_CLEAN = 0x0 const NS_WEAKENED = 0x1 const NS_INFECTED = 0x2 const NS_FLAGGED = 0x3 // Utils const hash = (x, y) => `${x},${y}` const unhash = (str) => str.split(',').map(n => parseInt(n)) const left = dir => dir + 90 const right = dir => dir - 90 const backout = dir => dir + 180 function main() { const partOne = ~~process.argv[2] === 1 const cluster = inputToCluster(input) let node = cluster['0,0'] const pos = { x: 0, y: 0 } let dir = 90 let infections = 0 let iter = ~~process.argv[3] || 10e6 while (iter--) { switch (node.status) { case NS_CLEAN: if (partOne) { node.status = NS_INFECTED infections++ } else { node.status = NS_WEAKENED } dir = left(dir) break case NS_WEAKENED: node.status = NS_INFECTED infections++ break case NS_INFECTED: if (partOne) { node.status = NS_CLEAN } else { node.status = NS_FLAGGED } dir = right(dir) break case NS_FLAGGED: node.status = NS_CLEAN dir = backout(dir) break } pos.x += Math.round(Math.cos(dtr(dir))) pos.y -= Math.round(Math.sin(dtr(dir))) const key = hash(pos.x, pos.y) let next = node[english(dir)] if (!next) { // unresolved, check if we've seen it: next = cluster[key] } if (!next) { // haven't seen it before, so record it next = new Node() // link back to this node next[english(backout(dir))] = node // bob is, indeed, your uncle cluster[key] = next } node = next } console.log(`Infections: ${infections}`) } /** * Quadtree node */ class Node { constructor( status = NS_CLEAN, up = null, down = null, left = null, right = null ) { this.up = up this.down = down this.left = left this.right = right this.status = status } } /** * Input string -> quadtree */ function inputToCluster(str) { const rows = str .split('\n') .map(row => row.split('').map(col => { switch (col) { case '#': return true default: return false } }) ) const cluster = {} const n = rows.length const off = -Math.floor(n/2) // halfway rows.forEach((row, _y) => { row.forEach((col, _x) => { const x = _x+off const y = _y+off const node = new Node(col ? NS_INFECTED : NS_CLEAN) cluster[hash(x, y)] = node }) }) for (const key in cluster) { resolve(key, cluster) } return cluster } function resolve(key, cluster) { const [x, y] = unhash(key) const node = cluster[key] if (!node.up) node.up = cluster[hash(x, y-1)] if (!node.down) node.down = cluster[hash(x, y+1)] if (!node.left) node.left = cluster[hash(x-1, y)] if (!node.right) node.right = cluster[hash(x+1, y)] } function english(dir) { switch (dir % 360) { case 90: return 'up' case 180: return 'left' case 270: return 'down' case 0: return 'right' } } function dtr(deg) { return deg*Math.PI/180 }