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 (4484B)


      1 #!/usr/bin/env node
      2 
      3 const fs = require('node:fs');
      4 
      5 const input = fs
      6   .readFileSync('input', 'utf-8')
      7   .split('\r\n').join('\n')
      8   .split('\r').join('\n')
      9   .split('\n')
     10   .filter(e => e)
     11   .map(line => line.split(',').map(n => parseInt(n,10)))
     12 
     13 let part1largest = -Infinity;
     14 let part2largest = -Infinity;
     15 
     16 let inputminx =  Infinity;
     17 let inputmaxx = -Infinity;
     18 let inputminy =  Infinity;
     19 let inputmaxy = -Infinity;
     20 
     21 // Build map of all vertical lines
     22 const verticals = [];
     23 for(let i=0; i<input.length; i++) {
     24   inputminx = Math.min(inputminx, input[i][0]);
     25   inputminy = Math.min(inputminy, input[i][1]);
     26   inputmaxx = Math.max(inputmaxx, input[i][0]);
     27   inputmaxy = Math.max(inputmaxy, input[i][1]);
     28 
     29   const current = i;
     30   const next    = (i+1) % input.length;
     31   if (input[current][1] === input[next][1]) continue;
     32   verticals.push([
     33     input[current][0],
     34     input[current][1],
     35     input[next][1],
     36   ]);
     37 }
     38 
     39 function is_inside(vector) {
     40   let inside = false;
     41   for (let vidx = 0; vidx < verticals.length; vidx++) {
     42     let vcur = verticals[vidx];
     43     let vnxt = verticals[(vidx+1)%verticals.length];
     44 
     45     // Handle ON the horizontal line
     46     if (
     47       (vector[1] === vcur[2] && vector[0] <= vcur[0] && vector[0] >= vnxt[0]) ||
     48       (vector[1] === vcur[2] && vector[0] <= vnxt[0] && vector[0] >= vcur[0])
     49     ) {
     50       return true;
     51     }
     52 
     53     if (vector[0] > vcur[0]) continue;
     54     if (vector[1] > vcur[1] && vector[1] > vcur[2]) continue;
     55     if (vector[1] < vcur[1] && vector[1] < vcur[2]) continue;
     56 
     57     // Handle ON the vertical line
     58     if (vector[0] === vcur[0]) {
     59       return true;
     60     }
     61 
     62     // Handle double-hit going same direction = inside
     63     if (
     64       vector[1] === vcur[2] &&
     65       vector[0]  <  vnxt[0] &&
     66       (
     67         ((vcur[1] < vector[1]) && (vector[1] < vnxt[2])) ||
     68         ((vcur[1] > vector[1]) && (vector[1] > vnxt[2]))
     69       )
     70     ) {
     71       // Skip, the next will catch it for being inside
     72       continue;
     73     }
     74 
     75     // Here = hit vertical
     76     inside = !inside;
     77   }
     78 
     79   return inside;;
     80 }
     81 
     82 for(const src of input) {
     83   next: for(const dst of input) {
     84     // console.log(`Checking ${src} -- ${dst}`);
     85     const width  = Math.abs(src[0] - dst[0]) + 1;
     86     const height = Math.abs(src[1] - dst[1]) + 1;
     87     const area   = width * height;
     88     part1largest = Math.max(part1largest, area);
     89 
     90     // Skip checking if area < current largest
     91     if (area <= part2largest) continue;
     92 
     93     // Short check
     94     const src_flipped = [ src[0], dst[1] ];
     95     const dst_flipped = [ dst[0], src[1] ];
     96     if (!is_inside(src_flipped)) {
     97       continue next;
     98     }
     99     if (!is_inside(dst_flipped)) {
    100       continue next;
    101     }
    102 
    103     const _src = [ Math.min(src[0], dst[0]), Math.min(src[1], dst[1]) ];
    104     const _dst = [ Math.max(src[0], dst[0]), Math.max(src[1], dst[1]) ];
    105 
    106     // Having a marked tile solidly within the square => implies gap within
    107     for(const test of input) {
    108       if (test[0] <= _src[0]) continue;
    109       if (test[0] >= _dst[0]) continue;
    110       if (test[1] <= _src[1]) continue;
    111       if (test[1] >= _dst[1]) continue;
    112       // Here = marked tile within square
    113       continue next;
    114     }
    115 
    116     // Only along edges, check every tile
    117     let covered = true;
    118 
    119     for(let y = _src[1] ; y <= _dst[1] ; y++) {
    120       if (!(
    121         is_inside([_src[0], y]) &&
    122         is_inside([_dst[0], y])
    123       )) {
    124         covered = false;
    125         break;
    126       }
    127     }
    128     // for(let x = _src[0] ; x <= _dst[0] ; x++) {
    129     //   if (!(
    130     //     is_inside([x, _src[1]]) &&
    131     //     is_inside([x, _dst[1]])
    132     //   )) {
    133     //     covered = false;
    134     //     break;
    135     //   }
    136     // }
    137     if (covered) {
    138       // Mark found square as if largest
    139       part2largest = Math.max(part2largest, area);
    140     }
    141 
    142   }
    143 }
    144 
    145 // // Debug display, gives an idea of the shape
    146 // (async () => {
    147 //   for(let y = inputminy; y <= inputmaxy ; y+=1024) {
    148 //     for(let x = inputminx; x <= inputmaxx ; x+=1024) {
    149 //     //   await new Promise(done => setTimeout(done, 1));
    150 //       const point = input.find(tile => tile[0] === x && tile[1] === y);
    151 //       if (point) {
    152 //         process.stdout.write('#');
    153 //       } else if (is_inside([x,y])) {
    154 //         process.stdout.write('.');
    155 //       } else {
    156 //         process.stdout.write(' ');
    157 //       }
    158 //     }
    159 //     process.stdout.write('\n');
    160 //   }
    161 // })();
    162 
    163 const part1 = part1largest;
    164 const part2 = part2largest;
    165 process.stdout.write(`------[ Part 1: ${part1} ]------\n`);
    166 process.stdout.write(`------[ Part 2: ${part2} ]------\n`);