function read(done) { require("fs").readFile('./input', 'utf8', (err, data) => { var map = new Map(), line = 0; data.split("\n").forEach((l) => { if (l && l.length) { while (true) { var reg = /[\^>" || reg[0] == "<") l = l.replace(reg[0], "-"); else l = l.replace(reg[0], "|"); map.carts.push(new Cart(reg.index, line, reg[0])); } map.push(l); ++line; } }); done(map); }); } function Map() { this.world = []; this.carts = []; } Map.prototype.show = function() { var data = []; this.world.forEach(row => { var str = ""; row.forEach(c => str += (c == '|' || c == '-' ? ' ' : c)); data.push(str); }); this.carts.forEach(c => { if (!data[c.py] || c.px >= data[c.py].length) return; data[c.py] = data[c.py].substring(0, c.px) +'x' +data[c.py].substring(c.px +1); }); data.forEach(row => { console.log(row); }); for (var i =data[0].length; i > 0; --i) process.stdout.write('-'); process.stdout.write("\n"); } Map.prototype.tick = function() { this.carts.sort((a, b) => { if (a.py == b.py) return a.px -b.px; return a.py -b.py; }); this.carts.some(c => { if (c.colided) return; c.move(this); var colide = this.colision(c.px, c.py); if (colide) { colide.forEach(c => c.colided = true); } }); } Map.prototype.nbCartAlive = function() { var live = 0; this.carts.forEach(c => c.colided ? live : ++live); return live; } Map.prototype.colision = function(px, py) { var colided = []; this.carts.forEach(c => { if (c.px == px && c.py == py && !c.colided) colided.push(c); }); return colided.length > 1 ? colided : null; } Map.prototype.isCross = function(px, py) { var c = this.world[py][px]; return c == '/' || c == '\\' || c == '+' ? c : null; } Map.prototype.push = function(line) { var l = []; for (var i =0, len = line.length; i < len; ++i) l[i] = line.charAt(i); this.world.push(l); } function Cart(px, py, direction) { this.px = px; this.py = py; this.colided = false; this.nextTurn = 0; this.id = px +"x" +py; switch (direction) { case '>': this.direction = [1, 0]; break; case '<': this.direction = [-1, 0]; break; case '^': this.direction = [0, -1]; break; case 'v': this.direction = [0, 1]; break; } } Cart.prototype.move = function(map) { this.px += this.direction[0]; this.py += this.direction[1]; var cross = map.isCross(this.px, this.py); if (!cross) return; if (cross == '+') { if (this.nextTurn == 0) this.direction = [ this.direction[1], -this.direction[0] ]; else if (this.nextTurn == 2) this.direction = [ -this.direction[1], this.direction[0] ]; this.nextTurn = (this.nextTurn +1) %3; } else { var factor = cross == '/' ? -1 : 1; this.direction = [ this.direction[1] *factor, this.direction[0] *factor ]; } } function ex1() { read(map => { while (map.nbCartAlive() == map.carts.length) map.tick(); map.carts.some(c => c.colided ? (console.log(c) || true) : null); }); } function ex2() { read(map => { while (map.nbCartAlive() > 1) map.tick(); map.carts.some(c => c.colided ? null : (console.log(c) || true)); }); } ex1(); ex2();