const { Glib, Vector, twodee } = require('../../lib'); const STATE = { EMPTY: 'L', FLOOR: '.', OCCUPIED: '#', NONE: 'X', }; const step1 = (input) => { let changes = 0n; const output = input.glibEntries .map(([pts, state]) => { switch (state) { case STATE.EMPTY: if ( Vector.fromString(pts) .neighbors() .map((pt) => pt.string) .lookupInMap(input, true) .filter((neighborState) => neighborState === STATE.OCCUPIED) .length === 0n ) { changes++; return [pts, STATE.OCCUPIED]; } return [pts, STATE.EMPTY]; case STATE.OCCUPIED: if ( Vector.fromString(pts) .neighbors() .map((pt) => pt.string) .lookupInMap(input, true) .filter((neighborState) => neighborState === STATE.OCCUPIED) .length >= 4n ) { changes++; return [pts, STATE.EMPTY]; } return [pts, STATE.OCCUPIED]; case STATE.FLOOR: return [pts, STATE.FLOOR]; default: console.log(pts, state); throw new Error('invalid state'); } }) .toMap(); return [output, changes]; }; const visibleNeighbor = (point, direction, state) => { let next = point.add(direction); while (true) { const nextString = next.string; if (!state.has(nextString)) { return state.NONE; } const found = state.get(nextString); if (found !== STATE.FLOOR) { return found; } next = next.add(direction); } }; const step2 = (input) => { let changes = 0n; const output = input.glibEntries .map(([pts, state]) => { const pt = Vector.fromString(pts); switch (state) { case STATE.EMPTY: if ( twodee.ORIGIN.neighbors() .map((direction) => visibleNeighbor(pt, direction, input)) .filter((neighborState) => neighborState === STATE.OCCUPIED) .length === 0n ) { changes++; return [pts, STATE.OCCUPIED]; } return [pts, STATE.EMPTY]; case STATE.OCCUPIED: if ( twodee.ORIGIN.neighbors() .map((direction) => visibleNeighbor(pt, direction, input)) .filter((neighborState) => neighborState === STATE.OCCUPIED) .length >= 5n ) { changes++; return [pts, STATE.EMPTY]; } return [pts, STATE.OCCUPIED]; case STATE.FLOOR: return [pts, STATE.FLOOR]; default: console.log(pts, state); throw new Error('invalid state'); } }) .toMap(); return [output, changes]; }; const solve = (input, stepFn) => { let state = Glib.fromLines(input) .flatMap((line, y) => Glib.fromIterable(line).map((cell, x) => [new Vector(x, y).string, cell]), ) .toMap(); // only run 1k iterations, it's a fairly simple automota // I wonder it it's possible to make a glider... for (let i = 0n; i < 1000n; i++) { const [newState, changes] = stepFn(state); if (changes === 0n) { return state.glibValues.filter((e) => e === STATE.OCCUPIED).length; } state = newState; } throw new Error('too many iterations'); }; module.exports = { 1: (input) => solve(input, step1), 2: (input) => solve(input, step2), };