Ben Ashton
1 year ago
2 changed files with 271 additions and 0 deletions
@ -0,0 +1,71 @@
|
||||
#.##..##.#..#.##..##....##..#..#####...#.###.#..##..#..#.#...##..##.... |
||||
##..###.#.#.#..##.##.####.........#...#..#..#.#..#.#....##..###.#.##..# |
||||
#.......###....#.#.##.#.#.#.##..###...###..##.....#...#.##.##.......##. |
||||
...##.#......#.#.#..#.###..#.###....#....#.###.##....####....##....##.. |
||||
.#...##..###...##.#.#..#...####.#.##.#.#..#.##.###.###.#.##.##..#.#...# |
||||
#..##....##.#..##.##...###..##.#...##...#####...#####.##..#.###....#.#. |
||||
###....#....#.##.####.###.#..####..##..##..#..#.##.#######.##....###... |
||||
###..###.....###..##..##....#......#.#.##...####.######..###.###....### |
||||
#.#.###.###.....###.####.#.####...#.#.###.#...#.##..##.##.#..####..#... |
||||
####.##...#....##.##.#.#....##.###....#.#..#.....##...##.#...###.##.#.# |
||||
...#.#..####.###...###.#....###...##.####..#.######.##.##..#.#..##...#. |
||||
#####......#.####.#####...#...#..#..#.#.#.#.#.#.#.#.##.#..#.###.#....#. |
||||
#..#.#.#..#...#.#.##..#.######.##..#.###...#.#.#...#.####..##..##..#.#. |
||||
..#.#..###....#.##.##....####.#########..#.########..##....#..#..##..#. |
||||
.#.#.#...##...##.#.#..#..#####..###....#..#...####.##.####.###.##..##.. |
||||
.##.####..#.#######..#.#######..##..###....#.#..#..##.#..#.#..###.....# |
||||
#....#.#####..#.##...###.###....#.#.#.#..#.##.####...#...##..#####.##.. |
||||
...##.###.##...#.##.###.#.###....####.....#.##.##.##.###...###.###.#.## |
||||
..###...#...##....#.###.....##..####..#.###.####..##.###....##.#....##. |
||||
...#..##..#..##.###.#....###....#..##..#...##.#..##.#.####...##.##..### |
||||
######.......####..#..#.#####.......#####..#.#..##..#......#.#.#.#####. |
||||
.#...#..#.##.###..#.#.#..####.#.########.#.###..####.#..#.#..###.#####. |
||||
...####.#..#..#.##...#.#####.########.##..##....#####.#.##..######.#.## |
||||
.....#..##.#.....#..#.#.##..######..####.#..#....##.....###.#.#..##..#. |
||||
..##.##..##.#.#.##...##.##...#..##.....#..#..#.#.#.....#.#.##.###...### |
||||
##.#..#.#.####.##....#...##.#...#...#..#.##..#...##.....#.#.##.#####... |
||||
...#..##..##...#..###....#..#.###..####...#.#..##.###.##......###.#.#.# |
||||
..##.#.#.##..###.....#..##.#....#....#....#.##..#.#.####.#....##..#..## |
||||
.##..##..#.#.....###..#.#..#.##.#.#.#..#..#.##.#.###...#..#..##.#....## |
||||
......#...######....###.##..#.###.####.##.##...#.###...####.##..#..#... |
||||
#..##.#.###.##......####...#.#.##.#.#....##.##..#.#.....####...#..###.. |
||||
..##...#.##..#.....#######.#.#.###.#.##...####..#.#....##.###..##.#.### |
||||
...####..#..##.########..#####.##..##...#..##..##..#...###.###....#.### |
||||
.###.#..#.##.#..###.#.##.#.#...#.#.#.##..##.#....###.#..#.....#..##..#. |
||||
...##.##...#..#.####.....#.##....####.#.###..##...#..#.####...##..##.## |
||||
###...##....##..##.#...##......#...##.##.#.....##..##.#..#.##......#..# |
||||
.......#.######...#####....#...#####.#####...##..#..#.#.#..........#.#. |
||||
#######.#...#.#..#..#..##..#.####..#.##..###..###.##.#...#.#..#.####.## |
||||
.#..#......#.#....###.#..##..####.#..#.#..##..#####.#....##.#####.#.#.# |
||||
.#.###.###..#..#.#.#.#..##.#......###.##.#....#######.#..#.#####.....## |
||||
#.####..###.#..###.#.#...##.##....###.#.#.#.###..#...#...#.##.###...#.. |
||||
#..##..##.##..##....###..######....#.#####.##..#...#....#.#.###.###.... |
||||
#.###..##.##..#.....##...#..#..#####.#.##..##..#.#...#.....##.#..#.##.# |
||||
##...#...#.##..####..#..#.###....#...#.....#.#.#.###...###...#####.#.## |
||||
.#.###.....#.#..#.###########.######..#########.##.##..#.#.#...#..##.## |
||||
##.#################.#.##.##....####.....#..#.#.##.###.##..#..#.##..#.# |
||||
.##.####.#.#..##.##.#.#..#.###.#..#..#.####....#.###.####.#.#..##..#### |
||||
##....###.#.####.#.#.............##.....###.##.#.###.####...#####.##.#. |
||||
...##.#.#.#####.####.##.#..##.###.###.####.#.#.....#.#.#...#####..####. |
||||
######.#....#...#.#.######.#.#....#.##..#..#..#..#.......#..#.##.#.#... |
||||
...#..#....#.#.#...###.####..##.#.#..#.#.#.####.#..###...###.######...# |
||||
#.#.###......##...#...##....###......#.###...#...###..#..####..##...#.# |
||||
#..##.....#........###.##..#.#.#.#.##..#.##.#..###.##.#.####..###.##.#. |
||||
.##....#..##.###.#.#.#..#..###..#..#####.##....#..#####.#.##..#.##.###. |
||||
.#.##.###.####.#.#..#.###.###.#.......####.##...#..##.#.##.##..#.###... |
||||
#.##...#####.#...#.#.##..###.#.##.###########..#...#..#.#.###.#.##..#.# |
||||
##...#...#.###.#..##..#...##....#...#.#....###.###.#.#..#...#.#.#.#.### |
||||
#.#.######.##.#....#.....#..##...#.#####...##.##...#..###.#...##.#..##. |
||||
##.#######.....#....#.###..#..#.###.###..#.....####....#.####..#.....#. |
||||
#..#..##..##.#.##......#....#.#.....#......#...##.###..#.#.###.......## |
||||
#..#.#..######.#.......##..#...#....#####.#...#.##...#...###..####.##.. |
||||
.....#.....#.#.##..###.###.##..#.##.##..#.#####..##.##..###..###..#.### |
||||
...#.###.#.#.#.##...##.##...#..#..#..####.#.##.#...#.#..##.#.#######.## |
||||
.####.####..#.####...#...#.#..##...##.##..#....#.#.###.###..#####...##. |
||||
..##.#.###.####.#####...#......#.#.###...###..##.##.###.####.###.#..#.# |
||||
#.##..####.#.#..#.##..#.##.#.##.#..###.####.#.#.#.##..#.....##.#.#...#. |
||||
.##...#.#.#####.###.#...###.#......#####.....#.#######.##.#..##...###.. |
||||
##.#.#####......####.####...#####....#..####.#.#.####...####.###.###.## |
||||
#.#.#.####..##.##.##.#.##.#.#####.##...#....##..##...#.######.....#..## |
||||
.#.....#...###.#.#.###.#..#...###...##.......####...####.###..#....#... |
||||
..##.##..#......###...##.....##.#..#...###.##.#.##.##...#.#...#.#..#... |
@ -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); |
Loading…
Reference in new issue