From b107ee70534f52feb16df10756a87cc258dc156e Mon Sep 17 00:00:00 2001 From: Ben Ashton Date: Sun, 1 Jan 2023 11:44:41 -0700 Subject: [PATCH] Day 23 --- 23/input | 71 +++++++++++++++++ 23/solution.mjs | 200 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 271 insertions(+) create mode 100644 23/input create mode 100644 23/solution.mjs diff --git a/23/input b/23/input new file mode 100644 index 0000000..e25be94 --- /dev/null +++ b/23/input @@ -0,0 +1,71 @@ +#.##..##.#..#.##..##....##..#..#####...#.###.#..##..#..#.#...##..##.... +##..###.#.#.#..##.##.####.........#...#..#..#.#..#.#....##..###.#.##..# +#.......###....#.#.##.#.#.#.##..###...###..##.....#...#.##.##.......##. +...##.#......#.#.#..#.###..#.###....#....#.###.##....####....##....##.. +.#...##..###...##.#.#..#...####.#.##.#.#..#.##.###.###.#.##.##..#.#...# +#..##....##.#..##.##...###..##.#...##...#####...#####.##..#.###....#.#. +###....#....#.##.####.###.#..####..##..##..#..#.##.#######.##....###... +###..###.....###..##..##....#......#.#.##...####.######..###.###....### +#.#.###.###.....###.####.#.####...#.#.###.#...#.##..##.##.#..####..#... +####.##...#....##.##.#.#....##.###....#.#..#.....##...##.#...###.##.#.# +...#.#..####.###...###.#....###...##.####..#.######.##.##..#.#..##...#. +#####......#.####.#####...#...#..#..#.#.#.#.#.#.#.#.##.#..#.###.#....#. +#..#.#.#..#...#.#.##..#.######.##..#.###...#.#.#...#.####..##..##..#.#. +..#.#..###....#.##.##....####.#########..#.########..##....#..#..##..#. +.#.#.#...##...##.#.#..#..#####..###....#..#...####.##.####.###.##..##.. +.##.####..#.#######..#.#######..##..###....#.#..#..##.#..#.#..###.....# +#....#.#####..#.##...###.###....#.#.#.#..#.##.####...#...##..#####.##.. +...##.###.##...#.##.###.#.###....####.....#.##.##.##.###...###.###.#.## +..###...#...##....#.###.....##..####..#.###.####..##.###....##.#....##. +...#..##..#..##.###.#....###....#..##..#...##.#..##.#.####...##.##..### +######.......####..#..#.#####.......#####..#.#..##..#......#.#.#.#####. +.#...#..#.##.###..#.#.#..####.#.########.#.###..####.#..#.#..###.#####. +...####.#..#..#.##...#.#####.########.##..##....#####.#.##..######.#.## +.....#..##.#.....#..#.#.##..######..####.#..#....##.....###.#.#..##..#. +..##.##..##.#.#.##...##.##...#..##.....#..#..#.#.#.....#.#.##.###...### +##.#..#.#.####.##....#...##.#...#...#..#.##..#...##.....#.#.##.#####... +...#..##..##...#..###....#..#.###..####...#.#..##.###.##......###.#.#.# +..##.#.#.##..###.....#..##.#....#....#....#.##..#.#.####.#....##..#..## +.##..##..#.#.....###..#.#..#.##.#.#.#..#..#.##.#.###...#..#..##.#....## +......#...######....###.##..#.###.####.##.##...#.###...####.##..#..#... +#..##.#.###.##......####...#.#.##.#.#....##.##..#.#.....####...#..###.. +..##...#.##..#.....#######.#.#.###.#.##...####..#.#....##.###..##.#.### +...####..#..##.########..#####.##..##...#..##..##..#...###.###....#.### +.###.#..#.##.#..###.#.##.#.#...#.#.#.##..##.#....###.#..#.....#..##..#. +...##.##...#..#.####.....#.##....####.#.###..##...#..#.####...##..##.## +###...##....##..##.#...##......#...##.##.#.....##..##.#..#.##......#..# +.......#.######...#####....#...#####.#####...##..#..#.#.#..........#.#. +#######.#...#.#..#..#..##..#.####..#.##..###..###.##.#...#.#..#.####.## +.#..#......#.#....###.#..##..####.#..#.#..##..#####.#....##.#####.#.#.# +.#.###.###..#..#.#.#.#..##.#......###.##.#....#######.#..#.#####.....## +#.####..###.#..###.#.#...##.##....###.#.#.#.###..#...#...#.##.###...#.. +#..##..##.##..##....###..######....#.#####.##..#...#....#.#.###.###.... +#.###..##.##..#.....##...#..#..#####.#.##..##..#.#...#.....##.#..#.##.# +##...#...#.##..####..#..#.###....#...#.....#.#.#.###...###...#####.#.## +.#.###.....#.#..#.###########.######..#########.##.##..#.#.#...#..##.## +##.#################.#.##.##....####.....#..#.#.##.###.##..#..#.##..#.# +.##.####.#.#..##.##.#.#..#.###.#..#..#.####....#.###.####.#.#..##..#### +##....###.#.####.#.#.............##.....###.##.#.###.####...#####.##.#. +...##.#.#.#####.####.##.#..##.###.###.####.#.#.....#.#.#...#####..####. +######.#....#...#.#.######.#.#....#.##..#..#..#..#.......#..#.##.#.#... +...#..#....#.#.#...###.####..##.#.#..#.#.#.####.#..###...###.######...# +#.#.###......##...#...##....###......#.###...#...###..#..####..##...#.# +#..##.....#........###.##..#.#.#.#.##..#.##.#..###.##.#.####..###.##.#. +.##....#..##.###.#.#.#..#..###..#..#####.##....#..#####.#.##..#.##.###. +.#.##.###.####.#.#..#.###.###.#.......####.##...#..##.#.##.##..#.###... +#.##...#####.#...#.#.##..###.#.##.###########..#...#..#.#.###.#.##..#.# +##...#...#.###.#..##..#...##....#...#.#....###.###.#.#..#...#.#.#.#.### +#.#.######.##.#....#.....#..##...#.#####...##.##...#..###.#...##.#..##. +##.#######.....#....#.###..#..#.###.###..#.....####....#.####..#.....#. +#..#..##..##.#.##......#....#.#.....#......#...##.###..#.#.###.......## +#..#.#..######.#.......##..#...#....#####.#...#.##...#...###..####.##.. +.....#.....#.#.##..###.###.##..#.##.##..#.#####..##.##..###..###..#.### +...#.###.#.#.#.##...##.##...#..#..#..####.#.##.#...#.#..##.#.#######.## +.####.####..#.####...#...#.#..##...##.##..#....#.#.###.###..#####...##. +..##.#.###.####.#####...#......#.#.###...###..##.##.###.####.###.#..#.# +#.##..####.#.#..#.##..#.##.#.##.#..###.####.#.#.#.##..#.....##.#.#...#. +.##...#.#.#####.###.#...###.#......#####.....#.#######.##.#..##...###.. +##.#.#####......####.####...#####....#..####.#.#.####...####.###.###.## +#.#.#.####..##.##.##.#.##.#.#####.##...#....##..##...#.######.....#..## +.#.....#...###.#.#.###.#..#...###...##.......####...####.###..#....#... +..##.##..#......###...##.....##.#..#...###.##.#.##.##...#.#...#.#..#... diff --git a/23/solution.mjs b/23/solution.mjs new file mode 100644 index 0000000..f227520 --- /dev/null +++ b/23/solution.mjs @@ -0,0 +1,200 @@ +import { readFileSync } from 'node:fs'; + +const input = readFileSync('input', 'utf-8'); + +export class InfiniteGrid { + constructor() { + this.ox = null; + this.oy = null; + this.rows = []; + } + + get height() { + return this.rows.length; + } + + get width() { + return this.rows[0]?.length ?? 0; + } + + get(x, y) { + return this.rows[y - (this.oy ?? y)]?.[x - (this.ox ?? x)]; + } + + set(x, y, v) { + this.expandToFit(x, y); + + const ax = x - this.ox; + const ay = y - this.oy; + + this.rows[ay][ax] = v; + } + + expandToFit(x, y) { + if (this.ox === null) this.ox = x; + if (this.oy === null) this.oy = y; + + const left = Math.max(0, this.ox - x); + const up = Math.max(0, this.oy - y); + const right = Math.max(0, (x - this.ox + 1) - this.width); + const down = Math.max(0, (y - this.oy + 1) - this.height); + + if (up || right || down || left) { + this.rows = [ + ...new Array(up).fill(new Array(this.width + left + right).fill()), + ...this.rows.map(row => + [...new Array(left), ...row, ...new Array(right)] + ), + ...new Array(down).fill(new Array(this.width + left + right).fill()) + ]; + } + + this.ox -= left; + this.oy -= up; + } + + getBounds() { + return { + minX: this.ox, + maxX: this.ox + (this.rows[0]?.length ?? 1) - 1, + minY: this.oy, + maxY: this.oy + (this.rows.length || 1) - 1 + }; + } + + printDefined() { + console.log(this.rows + .map(row => row + .map(cell => cell ? '#' : '.') + .join('') + ) + .join('\n') + ); + } + + countEmpty() { + return this.rows.reduce( + (total, row) => total + row.reduce( + (rowTotal, cell) => rowTotal + Number(!cell), + 0 + ), + 0 + ); + } +} + +class Elf { + constructor(grid) { + this.grid = grid; + this.pos = undefined; + } + + moveTo(x, y) { + if (this.pos) { + this.grid.set(...this.pos, undefined); + } + this.pos = [x, y]; + this.grid.set(...this.pos, this); + } + + proposePosition(directions) { + if (!this.pos) throw new Error('Elf is not on the grid'); + + const [x, y] = this.pos; + const N = this.grid.get(x, y - 1); + const NE = this.grid.get(x + 1, y - 1); + const E = this.grid.get(x + 1, y); + const SE = this.grid.get(x + 1, y + 1); + const S = this.grid.get(x, y + 1); + const SW = this.grid.get(x - 1, y + 1); + const W = this.grid.get(x - 1, y); + const NW = this.grid.get(x - 1, y - 1); + + if (!(N || NE || E || SE || S || SW || W || NW)) { + // Don't move + return; + } + + for (const direction of directions) { + if (direction === 'N' && !(NW || N || NE)) { + return [x, y - 1]; + } else if (direction === 'E' && !(NE || E || SE)) { + return [x + 1, y]; + } else if (direction === 'S' && !(SW || S || SE)) { + return [x, y + 1]; + } else if (direction === 'W' && !(NW || W || SW)) { + return [x - 1, y]; + } + } + } +} + +const elfPositions = input + .split('\n') + .map(line => line.trim()) + .filter(line => !!line) + .flatMap((line, y) => line + .split('') + .map((c, x) => { + if (c === '#') { + return [x, y]; + } + }) + .filter(coords => !!coords) + ); + +const grid = new InfiniteGrid(); +const elves = []; +for (const pos of elfPositions) { + const elf = new Elf(grid); + elves.push(elf); + elf.moveTo(...pos); +} +const directions = ['N', 'S', 'W', 'E']; +let round = 0; + +function processRound() { + const propositions = elves.flatMap(elf => { + const pos = elf.proposePosition(directions); + if (!pos) return []; + return [{elf, x: pos[0], y: pos[1]}]; + }); + + propositions.sort((p1, p2) => (p1.x - p2.x) || (p1.y - p2.y)); + const validPropositions = propositions + .reduce((groups, proposition) => { + if (groups.length) { + const lastGroup = groups[groups.length - 1]; + const {x: lastX, y: lastY} = lastGroup[0]; + if (proposition.x === lastX && proposition.y === lastY) { + lastGroup.push(proposition); + return groups; + } + } + + groups.push([proposition]); + return groups; + }, []) + .filter(group => group.length === 1) + .flat(); + + const moved = validPropositions.length; + + for (const proposition of validPropositions) { + proposition.elf.moveTo(proposition.x, proposition.y); + } + + // Reorder directions + directions.push(directions.shift()); + + round++; + return moved; +} + +do { + processRound(); +} while (round <= 10) +console.log('The number of empty ground tiles is:', grid.countEmpty()); + +while (processRound() !== 0) {}; +console.log('The number of rounds to completion is:', round);