// Run this by pasting it into the browser window of the URL "https://adventofcode.com/2025/day/9/input" // It is very slow, takeing multiple seconds { const response = await fetch(window.location.href) const input = await response.text() // const input = "7,1\n11,1\n11,7\n9,7\n9,5\n2,5\n2,3\n7,3" const points = [] for (const line of input.split("\n")) { const [x, y] = line.split(",") if (x) { const point = [parseInt(x), parseInt(y)] points.push(point) } } let parity = 0 { let p1 = points[points.length-2] let p2 = points[points.length-1] for (let i = 0; i < points.length; i++) { const p3 = points[i] if (p1[0] === p2[0] && p2[1] == p3[1]) { parity = parity - Math.sign((p2[1] - p1[1]) * (p3[0] - p2[0])) } else if (p1[1] === p2[1] && p2[0] == p3[0]) { parity = parity + Math.sign((p2[0] - p1[0]) * (p3[1] - p2[1])) } p1 = p2 p2 = p3 } } let rightIsInside = parity > 0; console.log(rightIsInside ? "Right is inside" : "Left is inside") let squares = [] for (const [i, p1] of points.entries()) { for (let j = 0; j < i; j++) { const p2 = points[j] const size = (Math.abs(p1[0] - p2[0]) + 1) * (Math.abs(p1[1] - p2[1]) + 1) squares.push([size, p1, p2]) } } squares.sort(([s1], [s2]) => s2 - s1) console.log("Answer part 1:", squares[0][0]) function cornersToRange(c1, c2) { return square = [ [Math.min(c1[0], c2[0]), Math.min(c1[1], c2[1])], [Math.max(c1[0], c2[0]), Math.max(c1[1], c2[1])] ] } function lineToVerticalRange(p1, p2) { if (p1[1] == p2[1]) { const lineParity = Math.sign(p2[0] - p1[0]) * parity return [ [Math.min(p1[0], p2[0]), lineParity > 0 ? p1[1] : - Infinity], [Math.max(p1[0], p2[0]), lineParity > 0 ? Infinity : p1[1]] ] } return null } function intersect([lb1, ub1], [lb2, ub2]) { const intersection = [ [Math.max(lb1[0], lb2[0]), Math.min(ub1[0], ub2[0])], [Math.max(lb1[1], lb2[1]), Math.min(ub1[1], ub2[1])] ] if (intersection[0][0] > intersection[1][0] || intersection[0][1] > intersection[1][1]) { return null } } function combineVerticalRange([lb1, ub1], [lb2, ub2]) { if (ub1[1] <= lb2[1] || ub2[1] <= lb1[1] || (ub1[1] > ub2[1] && lb1[1] < lb2[1]) || (ub2[1] > ub1[1] && lb2[1] < lb1[1])) { return [[[lb1, ub1]], [[lb2, ub2]], []] } const x1 = Math.max(lb1[0], lb2[0]) const x2 = Math.min(ub1[0], ub2[0]) if (x2 <= x1) { return [[[lb1, ub1]], [[lb2, ub2]], []] } const split1 = [] const split2 = [] const union = [] if (lb1[0] < lb2[0]) { split1.push([ [lb1[0], lb1[1]], [lb2[0], ub1[1]] ]) } else if (lb2[0] < lb1[0]) { split2.push([ [lb2[0], lb2[1]], [lb1[0], ub2[1]] ]) } if (ub2[0] > ub1[0]) { split2.push([ [ub1[0], lb2[1]], [ub2[0], ub2[1]] ]) } else if (ub1[0] > ub2[0]) { split1.push([ [ub2[0], lb1[1]], [ub1[0], ub1[1]] ]) } if (lb1[1] < lb2[1]) { if (lb1[1] > -Infinity) { split1.push([ [x1, lb1[1]], [x2, lb2[1]] ]) } } else if (lb2[1] < lb1[1]) { if (lb2[1] > -Infinity) { split2.push([ [x1, lb2[1]], [x2, lb1[1]] ]) } } const y1 = Math.max(lb1[1], lb2[1]) if (ub2[1] > ub1[1]) { if (ub2[1] < Infinity) { split2.push([ [x1, ub1[1]], [x2, ub2[1]] ]) } } else if (ub1[1] > ub2[1]) { if (ub1[1] < Infinity) { split1.push([ [x1, ub2[1]], [x2, ub1[1]] ]) } } const y2 = Math.min(ub1[1], ub2[1]) union.push([ [x1, y1], [x2, y2] ]) return [split1, split2, union] } function splitByYBound([lb, ub], [lyb, uyb]) { if (lyb > ub[1] || uyb < lb[1]) { return [[[lb, ub]], null] } const outside = [] if (lb[1] < lyb) { outside.push([ [lb[0], lb[1]], [ub[0], lyb - 1] ]) } if (ub[1] > uyb) { outside.push([ [lb[0], uyb + 1], [ub[0], ub[1]] ]) } const inside = [ [lb[0], Math.max(lb[1], lyb)], [ub[0], Math.min(ub[1], uyb)] ] return [outside, inside] } function splitByXBound([lb, ub], [lxb, uxb]) { if (lxb > ub[0] || uxb < lb[0]) { return [[[lb, ub]], null] } const outside = [] if (lb[0] < lxb) { outside.push([ [lb[0], lb[1]], [lxb - 1, ub[1]] ]) } if (ub[0] > uxb) { outside.push([ [uxb + 1, lb[1]], [ub[0], ub[1]] ]) } const inside = [ [Math.max(lb[0], lxb), lb[1]], [Math.min(ub[0], uxb), ub[1]] ] return [outside, inside] } function subtractRange([lb1, ub1], [lb2, ub2]) { const [outsideY, insideY] = splitByYBound([lb1, ub1], [lb2[1], ub2[1]]) if (insideY) { const [outsideX, _] = splitByXBound(insideY, [lb2[0], ub2[0]]) outsideY.push(...outsideX) } return outsideY } function combineLists(ranges1, ranges2) { for (let i = 0; i < ranges1.length; i++) { for (let j = 0; j < ranges2.length; j++) { const range1 = ranges1[i] const range2 = ranges2[j] if (!range1) { break } if (range2) { const [split1, split2, union] = combineVerticalRange(range1, range2) split1.push(...union) ranges1[i] = split1.pop() ranges2[j] = split2.pop() ranges1.push(...split1) ranges2.push(...split2) } } } ranges1.push(...ranges2) return ranges1.filter((x) => !!x) } function subtractLists(ranges1, ranges2) { for (let j = 0; j < ranges2.length; j++) { const range2 = ranges2[j] for (let i = 0; i < ranges1.length; i++) { const range1 = ranges1[i] if (!range1) { break } if (range2) { const res = subtractRange(range1, range2) ranges1[i] = res.pop() ranges1.push(...res) } } } return ranges1.filter((x) => !!x) } let ranges = [] let p1 = points[points.length-1] for (let i = 0; i < points.length; i++) { const p2 = points[i] const range = lineToVerticalRange(p1, p2) if (range) { ranges = combineLists(ranges, [range]) } p1 = p2 } for (const [s, c1, c2] of squares) { const empty = subtractLists([cornersToRange(c1, c2)], ranges) if (empty.length == 0) { console.log("Answer part 2:", s) break } } }