index.js (4238B)
1 const fs = require('fs'); 2 const through = require('through2'); 3 const line_by_line = require('../common/line-for-line'); 4 5 String.prototype.startsWith = function(subject) { 6 return this.substr(0, subject.length) === subject; 7 }; 8 9 const grid = []; 10 const entryPoint = [500, 0]; 11 12 function range(start, end) { 13 const ret = []; 14 const high = Math.max(start, end); 15 const low = Math.min(start, end); 16 for(let i=low; i<=high; i++) ret.push(i); 17 return low == start ? ret : ret.reverse(); 18 } 19 20 function plot(x, y, value = 1) { 21 grid[y] = grid[y] || []; 22 grid[y][x] = value; 23 } 24 25 // Pulled straight out of wikipedia 26 function plotLineLow(x0, y0, x1, y1) { 27 let dx = x1 - x0; 28 let dy = y1 - y0; 29 let yi = 1; 30 if (dy < 0) { 31 yi = -1; 32 dy = -dy; 33 } 34 let D = (2 * dy) - dx; 35 let y = y0; 36 for (const x of range(x0, x1)) { 37 plot(x, y); 38 if (D > 0) { 39 y = y + yi; 40 D = D + (2 * (dy - dx)); 41 } else { 42 D = D + 2*dy; 43 } 44 } 45 } 46 function plotLineHigh(x0, y0, x1, y1) { 47 let dx = x1 - x0; 48 let dy = y1 - y0; 49 let xi = 1; 50 if (dx < 0) { 51 xi = -1; 52 dx = -dx; 53 } 54 let D = (2 * dx) - dy; 55 let x = x0; 56 for (const y of range(y0, y1)) { 57 plot(x, y); 58 if (D > 0) { 59 x = x + xi; 60 D = D + (2 * (dx - dy)); 61 } else { 62 D = D + 2*dx; 63 } 64 } 65 } 66 function plotLine(x0, y0, x1, y1) { 67 if (Math.abs(y1 - y0) < Math.abs(x1 - x0)) { 68 if (x0 > x1) { 69 plotLineLow(x1, y1, x0, y0) 70 } else { 71 plotLineLow(x0, y0, x1, y1) 72 } 73 } else { 74 if (y0 > y1) { 75 plotLineHigh(x1, y1, x0, y0) 76 } else { 77 plotLineHigh(x0, y0, x1, y1) 78 } 79 } 80 } 81 82 let minx = entryPoint[0]; 83 let maxx = entryPoint[0]; 84 let miny = entryPoint[1]; 85 let maxy = entryPoint[1]; 86 87 function drawArea() { 88 // Draw area 89 for(let y=miny; y<=maxy; y++) { 90 for(let x=minx; x<=maxx; x++) { 91 if (grid[y] && grid[y][x] == 1) { 92 process.stdout.write('#'); 93 } else if (grid[y] && grid[y][x] == 2) { 94 process.stdout.write('+'); 95 } else if (grid[y] && grid[y][x] == 3) { 96 process.stdout.write('o'); 97 } else { 98 process.stdout.write('.'); 99 } 100 } 101 process.stdout.write('\n'); 102 } 103 } 104 105 106 107 let units = 0; 108 109 fs.createReadStream('input') 110 .pipe(line_by_line()) 111 112 // Minor parsing 113 .pipe(through(function(line, enc, cb) { 114 line = line.toString(); 115 116 // Turn into line point coordinates 117 const points = line 118 .split(' -> ') 119 .map(point => point.split(',').map(a => parseInt(a))); 120 121 // Track range to visualize later 122 points.forEach(point => { 123 minx = Math.min(minx, point[0]); 124 miny = Math.min(miny, point[1]); 125 maxx = Math.max(maxx, point[0]); 126 maxy = Math.max(maxy, point[1]); 127 }); 128 129 // "draw" lines 130 let lastPoint = points.shift(); 131 while(points.length) { 132 const point = points.shift(); 133 plotLine( 134 lastPoint[0], lastPoint[1], 135 point[0], point[1], 136 ); 137 lastPoint = point; 138 } 139 140 cb(); 141 })) 142 143 .on('finish', () => { 144 145 // Mark entrypoint 146 plot(entryPoint[0], entryPoint[1], 2); 147 148 // Mark floor 149 maxy+=2; 150 plotLine( 151 minx-10000, maxy, 152 maxx+10000, maxy, 153 ); 154 minx -= 10; 155 maxx += 10; 156 157 // Let sand fall in 158 while(true) { 159 160 let unit = [...entryPoint]; 161 162 while(true) { 163 if (unit[1] > maxy) { 164 break; 165 } 166 if (!(grid[unit[1]+1])) { 167 unit[1]++; 168 } else if (!(grid[unit[1]+1][unit[0]])) { 169 unit[1]++; 170 } else if (!(grid[unit[1]+1][unit[0]-1])) { 171 unit[0]--; 172 unit[1]++; 173 } else if (!(grid[unit[1]+1][unit[0]+1])) { 174 unit[0]++; 175 unit[1]++; 176 } else { 177 // rest 178 break; 179 } 180 } 181 182 plot(unit[0], unit[1], 3); 183 units++; 184 185 if (units%100 == 0) { 186 drawArea(); 187 process.stdout.write('\n'.repeat(3)); 188 } 189 190 if ( 191 (unit[1] > maxy) || 192 (unit[0] == entryPoint[0] && unit[1] == entryPoint[1]) 193 ) { 194 break; 195 } 196 } 197 198 199 drawArea(); 200 console.log({ units }); 201 console.log('finish'); 202 });