|
|
@@ -0,0 +1,165 @@
|
|
|
+
|
|
|
+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();
|
|
|
+
|
|
|
+
|