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