Array.prototype.count = function(fnc) { return this.reduce((total, i, index) => fnc(i, index) ? total+1 : total, 0); } Array.prototype.countUnique = function(fnc) { return this.reduce((total, i, index) => (total.indexOf(i) === -1 && fnc(i, index)) ? total.concat(i):total, []).length; } const COLOR_RESET = '\x1B[39m'; const CLEAR_SCR = '\033[2J'; const COLOR_MAP = [ '\033[31m', '\033[32m', '\033[33m', '\033[35m', '\033[36m' ]; const chars = ['@', '!', '$', '%', '+', '=', '?', '#']; function Elf(px, py) { this.px = px; this.py = py; this.nextX = 0; this.nextY = 0; this.moveList = [ { cond: (surrounding) => !surrounding[0][0] && !surrounding[1][0] && !surrounding[2][0], action: (self) => { self.nextX = self.px; self.nextY = self.py -1; } }, { cond: (surrounding) => !surrounding[0][2] && !surrounding[1][2] && !surrounding[2][2], action: (self) => { self.nextX = self.px; self.nextY = self.py +1; } }, { cond: (surrounding) => !surrounding[0][0] && !surrounding[0][1] && !surrounding[0][2], action: (self) => { self.nextX = self.px -1; self.nextY = self.py; } }, { cond: (surrounding) => !surrounding[2][0] && !surrounding[2][1] && !surrounding[2][2], action: (self) => { self.nextX = self.px +1; self.nextY = self.py; } } ]; this.color = COLOR_MAP[Math.floor(Math.random() * COLOR_MAP.length)]; this.c = chars[Math.floor(Math.random() * chars.length)]; this.done = false; } Elf.prototype.round = function(pool, roundId) { const surrounding = pool.surround(this.px, this.py); if (surrounding._count === 0) { this.nextX = this.px; this.nextY = this.py; } else for (let i =0; i < 4; ++i) { if (this.moveList[(i + roundId) % this.moveList.length].cond(surrounding)) { this.moveList[(i + roundId) % this.moveList.length].action(this); return; } } this.nextX = this.px; this.nextY = this.py; } function ElfPool() { this.elves = []; this.boundaries = [[Infinity, -Infinity], [Infinity, -Infinity]]; } ElfPool.prototype.dopush = function(px, py) { this.elves.push(new Elf(px, py)); this.boundaries = [ [Math.min(this.boundaries[0][0], px), Math.max(this.boundaries[0][1], px)], [Math.min(this.boundaries[1][0], py), Math.max(this.boundaries[1][1], py)]]; } ElfPool.prototype.round = function(roundId) { for (let i of this.elves) i.round(this, roundId); if (this.elves.every(x => x.px === x.nextX && x.py === x.nextY)) return true; this.elves.forEach(i => i.done = false); this.boundaries = [[Infinity, -Infinity], [Infinity, -Infinity]]; for (let i =0; i < this.elves.length; ++i) { if (!this.elves[i].done) for (let j =i +1; j < this.elves.length; ++j) { if (this.elves[i].nextX === this.elves[j].nextX && this.elves[i].nextY === this.elves[j].nextY) this.elves[i].done = this.elves[j].done = true; } if (!this.elves[i].done) { this.elves[i].px = this.elves[i].nextX; this.elves[i].py = this.elves[i].nextY; this.boundaries = [ [Math.min(this.boundaries[0][0], this.elves[i].px), Math.max(this.boundaries[0][1], this.elves[i].px)], [Math.min(this.boundaries[1][0], this.elves[i].py), Math.max(this.boundaries[1][1], this.elves[i].py)]]; } } return false; } ElfPool.prototype.surround = function(px, py) { let result = [ [ false, false, false], [ false, false, false ], [ false, false, false ]]; result._count = 0; for (let i = px-1; i <= px+1; ++i) for (let j=py -1; j <= py +1; ++j) if ((i !== px || j !== py) && this.elves.some(x => x.py === j && x.px === i)) { result[i-px+1][j-py+1] = true; result._count++; } return result; } ElfPool.prototype.display = async function() { let minY = this.boundaries[1][0]; let minX = this.boundaries[0][0]; let maxX = this.boundaries[0][1]; let elvesRows = []; console.log(CLEAR_SCR); for (let i of this.elves) { if (!elvesRows[i.py - minY]) elvesRows[i.py - minY] = []; elvesRows[i.py - minY].push({x: i.px, color: i.color, c: i.c }); } for (let i =0; i < elvesRows.length; ++i) { _i = (elvesRows[i] || []).sort((a, b) => a.x-b.x); let line = ''; for (let pos = minX; pos <= maxX; ++pos) { if (_i[0]?.x === pos) { line += _i[0].color+_i[0].c+COLOR_RESET; _i.shift(); } else { line += '.'; } } console.log(line); } return new Promise((ok, ko) => setTimeout(ok, 450)); } let _main = (async()=>{ let pool = new ElfPool(); let lineIndex = 0; for await (let line of require('readline').createInterface({ input: process.stdin })) { for (let i =0; i < line.length; ++i) if (line[i] === '#') pool.dopush(i, lineIndex); ++lineIndex; } let i =0; let part1Solution = 0; while (true) { if (i === 10) part1Solution = ((pool.boundaries[0][1]-pool.boundaries[0][0]+1) * (pool.boundaries[1][1]-pool.boundaries[1][0]+1)) - pool.elves.length; if (pool.round(i)) break; ++i; await pool.display(); } await pool.display(); console.log("Part 1: ", part1Solution); console.log("Part 2: ", ++i); }); // response < 4241 // > 4076 _main();