|
|
@@ -0,0 +1,130 @@
|
|
|
+
|
|
|
+const fs = require('fs');
|
|
|
+const readline = require('readline');
|
|
|
+
|
|
|
+function Rope(len) {
|
|
|
+ this.tokens = [];
|
|
|
+ this.visited = {'0x0': true};
|
|
|
+ this.visitedCoord = [[0, 0]];
|
|
|
+ this.boundaries = [[0, 0], [0, 0]];
|
|
|
+ this.len = len;
|
|
|
+ for (let i =0; i < len +1; ++i)
|
|
|
+ this.tokens.push([0, 0]);
|
|
|
+}
|
|
|
+
|
|
|
+Rope.prototype.applyMove = function(moveArr) {
|
|
|
+ this.tokens[0][0] += moveArr[0];
|
|
|
+ this.tokens[0][1] += moveArr[1];
|
|
|
+ this.boundaries = [
|
|
|
+ [ Math.min(this.boundaries[0][0], this.tokens[0][0]), Math.max(this.boundaries[0][1], this.tokens[0][0]) ],
|
|
|
+ [ Math.min(this.boundaries[1][0], this.tokens[0][1]), Math.max(this.boundaries[1][1], this.tokens[0][1]) ],
|
|
|
+ ];
|
|
|
+ for (let i =0; i < this.tokens.length -1; ++i)
|
|
|
+ this.checkAndMove(i);
|
|
|
+ this.visited[this.tokens[this.tokens.length -1][0] + 'x' +this.tokens[this.tokens.length -1][1]] = true;
|
|
|
+ this.visitedCoord.push([this.tokens[this.tokens.length -1][0], this.tokens[this.tokens.length -1][1]]);
|
|
|
+}
|
|
|
+
|
|
|
+Rope.prototype.checkAndMove = function(headPos) {
|
|
|
+ if (Math.abs(this.tokens[headPos][0] - this.tokens[headPos +1][0]) <= 1 &&
|
|
|
+ Math.abs(this.tokens[headPos][1] - this.tokens[headPos+1][1]) <= 1)
|
|
|
+ return;
|
|
|
+ if (this.tokens[headPos +1][1] !== this.tokens[headPos][1])
|
|
|
+ this.tokens[headPos +1][1] += (this.tokens[headPos +1][1] < this.tokens[headPos][1]) ? 1 : -1;
|
|
|
+ if (this.tokens[headPos +1][0] !== this.tokens[headPos][0])
|
|
|
+ this.tokens[headPos +1][0] += (this.tokens[headPos +1][0] < this.tokens[headPos][0]) ? 1 : -1;
|
|
|
+}
|
|
|
+
|
|
|
+const COLORS = [ '\033[39m', '\033[31m', '\033[32m', '\033[33m', '\033[34m', '\033[35m', '\033[36m' ]
|
|
|
+
|
|
|
+function showRope(arenaSize, ropeItems) {
|
|
|
+ // clear screen
|
|
|
+ for (let i =0; i < 5; ++i) console.log();
|
|
|
+ // /clear screen
|
|
|
+
|
|
|
+ console.log('\x1Bc');
|
|
|
+ let item = ropeItems.shift();
|
|
|
+ for (let py =0; py < arenaSize[1]; ++py) {
|
|
|
+ let line = '';
|
|
|
+ for (let px =0; px < arenaSize[0]; ++px) {
|
|
|
+ if (item && item.x === px && item.y === py) {
|
|
|
+ line += COLORS[item.color] + item.c + COLORS[0];
|
|
|
+ item = ropeItems.shift();
|
|
|
+ } else {
|
|
|
+ line += '.';
|
|
|
+ }
|
|
|
+ }
|
|
|
+ console.log(line);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function prepShowRope(arenaSize, arenaShift, replayRope) {
|
|
|
+ showRope(arenaSize, replayRope.tokens
|
|
|
+ .map((i, index) => { return {
|
|
|
+ x: i[0]+arenaShift[0],
|
|
|
+ y: i[1]+arenaShift[1],
|
|
|
+ c: '#',
|
|
|
+ color: index+1 >= COLORS.length ? Math.ceil(Math.random() * (COLORS.length-1)) : (index +1)
|
|
|
+ };}).concat(
|
|
|
+ replayRope.visitedCoord.map(i => { return {
|
|
|
+ x: i[0] + arenaShift[0],
|
|
|
+ y: i[1] + arenaShift[1],
|
|
|
+ c: '"',
|
|
|
+ color: 0
|
|
|
+ };})
|
|
|
+ )
|
|
|
+ .sort((a, b) => {
|
|
|
+ if (a.x === b.x && a.y === b.y)
|
|
|
+ return a.c === '"' ? 1 : -1;
|
|
|
+ if (a.y === b.y)
|
|
|
+ return a.x-b.x;
|
|
|
+ return a.y-b.y;
|
|
|
+ })
|
|
|
+ .filter((value, index, self) => self.findIndex(i => i.x === value.x && i.y === value.y) === index));
|
|
|
+}
|
|
|
+
|
|
|
+function mSleep() {
|
|
|
+ return new Promise(ok => setTimeout(ok, 90));
|
|
|
+}
|
|
|
+
|
|
|
+async function showReplay(rope, actions) {
|
|
|
+ let arenaSize = [ rope.boundaries[0][1] - rope.boundaries[0][0] +1, rope.boundaries[1][1] - rope.boundaries[1][0] +1];
|
|
|
+ let arenaShift = [ -rope.boundaries[0][0], -rope.boundaries[1][0] ];
|
|
|
+ let replayRope = new Rope(rope.len);
|
|
|
+
|
|
|
+ await prepShowRope(arenaSize, arenaShift, replayRope);
|
|
|
+ for (let i of actions) {
|
|
|
+ await mSleep();
|
|
|
+ replayRope.applyMove(i);
|
|
|
+ prepShowRope(arenaSize, arenaShift, replayRope);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+async function main() {
|
|
|
+ const move = {
|
|
|
+ 'U': [0, -1],
|
|
|
+ 'D': [0, 1],
|
|
|
+ 'L': [-1, 0],
|
|
|
+ 'R': [1, 0]
|
|
|
+ };
|
|
|
+ let tokens = new Rope(1);
|
|
|
+ let tokensPart2 = new Rope(10);
|
|
|
+ let moveList = [];
|
|
|
+ for await (let line of readline.createInterface({ input: process.stdin })) {
|
|
|
+ if (!line || !line.length)
|
|
|
+ continue;
|
|
|
+ let reg = /([UDLR]) (\d+)/.exec(line);
|
|
|
+ for (let i =0; i < reg[2]; ++i) {
|
|
|
+ moveList.push(move[reg[1]]);
|
|
|
+ tokens.applyMove(move[reg[1]]);
|
|
|
+ tokensPart2.applyMove(move[reg[1]]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ await showReplay(tokensPart2, moveList);
|
|
|
+
|
|
|
+ console.log("Part 1: ", Object.keys(tokens.visited).length);
|
|
|
+ console.log("Part 2: ", Object.keys(tokensPart2.visited).length);
|
|
|
+};
|
|
|
+
|
|
|
+(main());
|
|
|
+
|