-
Notifications
You must be signed in to change notification settings - Fork 0
/
day15.js
114 lines (93 loc) · 3.34 KB
/
day15.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
import { parseLinesFromFile, writeAnswer } from './helpers.js';
const NUMBER_REGEX = /-?\d+/g;
function parseLine(str) {
const matches = str.match(NUMBER_REGEX)
const [sensorX, sensorY, beaconX, beaconY] = matches;
return {
sensorX: parseInt(sensorX),
sensorY: parseInt(sensorY),
beaconX: parseInt(beaconX),
beaconY: parseInt(beaconY),
};
}
function calculateManhattanDistance(startX, startY, endX, endY) {
return Math.abs(startX-endX) + Math.abs(startY-endY);
}
function parseFile(filename) {
return parseLinesFromFile(filename).map(line => {
const { sensorX, sensorY, beaconX, beaconY } = parseLine(line);
const distance = calculateManhattanDistance(sensorX, sensorY, beaconX, beaconY);
return {
sensor: { x: sensorX, y: sensorY },
beacon: { x: beaconX, y: beaconY },
distance,
}
});
}
function getSensorsRowCoverage(sensors, row) {
return sensors.map(({ sensor, distance }) => getSensorCoverage(distance, sensor, row)).filter(Boolean);
}
function getSensorCoverage(distance, sensor, row) {
const width = distance - Math.abs(sensor.y - row);
if (width < 0) return;
return [sensor.x - width, sensor.x + width];
}
function getDistressCoordinate(sensors, maxRow) {
for (let y=0; y<maxRow; y++) {
const rowCoverage = getRowCoverage(sensors, y);
// If there is a whole in the merged coverage line then that's where the distress signal is
if (rowCoverage.length > 1) {
// is's the position in between the two coverage spots
return [rowCoverage[0][1] + 1, y];
}
}
}
function mergeCoverage(ranges) {
let merged = [];
ranges.sort((a, b) => a[0] - b[0]);
let working = ranges[0];
for (let i=0; i<ranges.length; i++) {
let curr = ranges[i];
// last one overlaps the current one
if (working[1] >= curr[0] - 1) {
working = [working[0], Math.max(working[1], curr[1])];
} else {
// reached the end of a set of overlapping ranges
merged.push(working);
working = curr;
}
}
// add last one
merged.push(working);
return merged;
}
function getRowCoverage(sensorData, targetRow) {
const coverage = getSensorsRowCoverage(sensorData, targetRow);
return mergeCoverage(coverage);
}
function getBlockedItemsInRow(merged) {
return merged.reduce((total, [start,end]) => {
return total + (end - start);
}, 0);
}
function getTuningFrequency(x, y) {
return x*4000000 + y;
}
function solveProblem1(isSample=true) {
const filename = isSample ? './day15.sample.txt' : './day15.puzzle.txt';
const targetRow = isSample ? 10 : 2000000;
const data = parseFile(filename);
const merged = getRowCoverage(data, targetRow);
const answer = getBlockedItemsInRow(merged); // 0 and beacon position offset each other to make this work....
writeAnswer(answer);
}
function solveProblem2(isSample=true) {
const filename = isSample ? './day15.sample.txt' : './day15.puzzle.txt';
const maxRow = isSample ? 20 : 4000000;
const data = parseFile(filename);
const coord = getDistressCoordinate(data, maxRow);
const answer = getTuningFrequency(coord[0], coord[1]);
writeAnswer(answer, 2);
}
solveProblem1(false);
solveProblem2(false);