Advent-of-Code-2022/15/solution.mjs

105 lines
2.9 KiB
JavaScript

import { readFileSync } from 'node:fs';
const input = readFileSync('input', 'utf-8');
const sensors = input
.split('\n')
.filter(Boolean)
.map(line => {
const match = line.match(/.*?x=(-?\d+), y=(-?\d+).*?x=(-?\d+), y=(-?\d+)/);
if (!match) {
throw new Error(`Invalid sensor readout: ${line}`);
}
const [x, y, bx, by] = match
.slice(1)
.map(n => parseInt(n));
return {x, y, bx, by};
});
function rangesOverlap(range1, range2, tollerance=0) {
const t = tollerance;
range1 = [...range1].sort((a, b) => a - b);
range2 = [...range2].sort((a, b) => a - b);
return (
(range1[0] >= range2[0] - t && range1[0] <= range2[1] + t) ||
(range2[0] >= range1[0] - t && range2[0] <= range1[1] + t)
);
}
function mergeCoverage(coverage) {
return [...coverage]
.map(range => [...range].sort((a, b) => a - b))
.sort((a, b) => a[0] - b[0])
.reduce((newCoverage, range) => {
if (newCoverage.length) {
const lastRange = newCoverage[newCoverage.length - 1];
if (rangesOverlap(lastRange, range, 1)) {
newCoverage[newCoverage.length - 1] = [
Math.min(lastRange[0], range[0]),
Math.max(lastRange[1], range[1])
];
return newCoverage;
}
}
newCoverage.push(range);
return newCoverage;
}, []);
}
function getCoverageForRow(y) {
const coverage = [];
for (const s of sensors) {
const range = Math.abs(s.x - s.bx) + Math.abs(s.y - s.by);
const distanceToSensor = Math.abs(s.y - y);
const reducedRange = range - distanceToSensor;
if (reducedRange < 0) continue;
coverage.push([s.x - reducedRange, s.x + reducedRange]);
}
return mergeCoverage(coverage);
}
function countBeaconsForRow(y) {
return sensors
.map(s => [s.bx, s.by])
.filter((coord, i, coords) => coords.findLastIndex(
c => c[0] === coord[0] && c[1] === coord[1]
) === i)
.reduce((total, coord) => total + Number(coord[1] === y), 0);
}
function countCoverageForRow(y) {
const coverageCount = getCoverageForRow(y)
.reduce((total, range) => total + range[1] - range[0] + 1, 0);
const beaconCount = countBeaconsForRow(y);
return coverageCount - beaconCount;
}
const row = 2000000;
const coverageCount = countCoverageForRow(2000000);
console.log(
`There are ${coverageCount} positions that cannot contain a beacon on ` +
`row: ${row}`
);
function limitCoverage(coverage, min, max) {
return coverage
.filter(range => rangesOverlap([min, max], range))
.map(range => [Math.max(min, range[0]), Math.min(max, range[1])]);
}
console.log('Locating distress beacon...');
let tuningFrequency;
for (let y = 0; y <= 4000000; y++) {
const coverage = limitCoverage(getCoverageForRow(y), 0, 4000000);
if (coverage.length > 1) {
const x = coverage[0][1] + 1;
tuningFrequency = x * 4000000 + y;
}
}
console.log(`Tuning frequency for distress beacon: ${tuningFrequency}`);