const fs = require('fs'); const readline = require('readline'); const MAP_WIDTH = 7; const RESET_COLOR = '\x1B[39m'; const EMPTY_CHAR = ' ';//'\x1B[39m.'; function CircularBuffer(dataArr) { this.data = dataArr; this.pos = 0; } CircularBuffer.prototype.pop = function() { let data = this.data[this.pos++]; while (this.pos >= this.data.length) this.pos -= this.data.length; return data; } function Arena() { this.current = null; this.data = [[]]; this.dataBit = [ 0xFF ]; this.dejavu = []; this.heightRemoved = 0; for (let i =0; i < MAP_WIDTH; ++i) this.data[0].push('-'); } Arena.prototype.currentCover = function(px, py) { if (!this.current || this.current.x + this.current.width <= px || this.current.x > px || this.current.y >= py || this.current.y + this.current.data.length < py) return false; return this.current.data[py - this.current.y -1][px-this.current.x]; } Arena.prototype.display = function(size) { if (!size) return Promise.resolve(); console.log('\033[2J'); for (let i =50; i >= 0; --i) { let border = i === 0 ? '+' : '|'; let line = ""; for (let j =0; j < MAP_WIDTH; ++j) { if (this.currentCover(j, i)) line += this.current.color + this.current.c; else if (this.data[i] && this.data[i][j]) line += (typeof this.data[i][j] === 'string' ? this.data[i][j] : (this.data[i][j].color + this.data[i][j].c)); else line += EMPTY_CHAR; } console.log(border+line+RESET_COLOR+border); } return new Promise((ok, ko) => setTimeout(ok, 75)); } Arena.prototype.canMove = function(dx, dy) { if (this.current.x + dx < 0 || this.current.x + dx >= MAP_WIDTH || this.current.y + dy < 0) return false; return this.current.data .every((bitSet, index) => { if (dx === -1) bitSet <<= 1; else if (dx === 1) bitSet >>= 1; console.log("can move ? ", this.current.height-index+dy+this.current.y, this.dataBit[this.current.height-index+dy+this.current.y], bitSet); return (this.dataBit[this.current.height-index+dy+this.current.y] || 0x00) & bitSet != 0; }); } Arena.prototype.move = async function(dx, dy, displaySize) { await this.display(displaySize); if (!this.canMove(dx, dy)) return false; this.current.x += dx; this.current.y += dy; return true; } Arena.prototype.stackSize = function(pos) { for (let i = this.dataBit.length-1; i > 0; --i) { if (this.dataBit[i][1 << pos]) return i <= 0 ? Infinity : i; } return Infinity; } Arena.prototype.optimHeight = function() { let max = Infinity; for (let i =0; i < MAP_WIDTH; ++i) max = Math.min(max, this.stackSize(i)); if (max !== Infinity) { this.heightRemoved += max; while (max-- > 0) { this.data.shift(); this.dataBit.shift(); } } } Arena.prototype.nextRock = async function(pieces, jetStream, displaySize, round) { const colors = [ '\033[31m', '\033[32m', '\033[33m', '\033[35m', '\033[36m' ]; const chars = ['@', '!', '$', '%', '+', '=', '?', '#']; let piece = pieces.pop(); let currentData = piece.map(i => i.split('').map(i => i === '#')); this.current = { data: piece.map(i => i.split('').map(i => i === '#')), color: colors[Math.floor(Math.random() * colors.length)], x: 2, y: this.data.length +2, c: chars[Math.floor(Math.random() * chars.length)], height: 0, width: 0 }; let currentBits = []; for (let i of this.current.data) { let b = 0x00; for (let j =0; j < i.length; ++j) b |= i[j]< line.map((data, x) => data ? [x+this.current.x, this.current.height-y+this.current.y] : undefined)) .reduce((acc, i) => (acc || []).concat(i)) .filter(i => !!i) .forEach(i => { while (this.data.length <= i[1]) { this.data.push([]); this.dataBit.push(0x00); } this.data[i[1]][i[0]] = { color: this.current.color, c: this.current.c }; this.dataBit[i[1]] |= (1 << i[0]); }); this.current = null; this.dejavu[pieces.pos+'x'+jetStream.pos] = { piece: pieces.pos, jetStream: jetStream.pos, index: round }; (round %500 === 0) && this.optimHeight(); await this.display(displaySize); } const SCREEN_SIZE = 60; //const ProgressBar = require('progress'); async function main() { let arena = new Arena(); let jetStream = null; let pieces = new CircularBuffer([ ["####"], [" # ", "###", " # "], [" #", " #", "###"], ["#", "#", "#", "#"], ["##", "##"] ]); for await (let line of readline.createInterface({ input: process.stdin })) { jetStream = new CircularBuffer(line.split('').map(i => i === '<' ? -1 : +1)); break; } let i =0; for (; i < 2022; ++i) await arena.nextRock(pieces, jetStream, 0, i); console.log("Part 1", arena.heightRemoved + arena.data.length -1); return; //let Progress = new ProgressBar(':bar', { total: 1000000000000 }); for (; i < 1000000000000; ++i) { arena.nextRock(pieces, jetStream, 0, i); //Progress.tick(); } console.log("\n\nPart 2", arena.heightRemoved + arena.data.length -1); }; (main());