advent-of-code

Entries to advent of code, multiple years
git clone git://git.finwo.net/misc/advent-of-code
Log | Files | Refs

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   });