import { readFileSync } from 'node:fs'; let sampleMode = false; let usedArray = []; const sampleArray = readFileSync('sample.txt').toString().split("\n"); const inputArray = readFileSync('input.txt').toString().split("\n"); if (sampleMode) { usedArray = sampleArray; } else { usedArray = inputArray; } // Part One console.time("part1"); let coordinatesArray = []; for (const element of usedArray) { const coordinates = element.split(","); coordinatesArray.push({ x: Number.parseInt(coordinates[0]), y: Number.parseInt(coordinates[1]) }); } let segments = []; for (let i = 0; i < coordinatesArray.length; i++) { for (let j = i + 1; j < coordinatesArray.length; j++) { const dx = Math.abs(coordinatesArray[i].x - coordinatesArray[j].x) + 1; const dy = Math.abs(coordinatesArray[i].y - coordinatesArray[j].y) + 1; const area = dx * dy; segments.push({ dx: dx, dy: dy, area: area }); } } segments.sort((a, b) => a.area > b.area ? -1 : 1); console.timeEnd("part1"); console.log(segments[0].area); // Part Two console.time("part2"); // Only collect the RED tile coordinates (endpoints) const xSet = new Set(); const ySet = new Set(); for (const tile of coordinatesArray) { xSet.add(tile.x); ySet.add(tile.y); } const xCoords = [...xSet].sort((a, b) => a - b); const yCoords = [...ySet].sort((a, b) => a - b); // Store horizontal and vertical line segments for boundary checking const hLines = []; // {y, x1, x2} const vLines = []; // {x, y1, y2} for (let i = 0; i < coordinatesArray.length; i++) { const curr = coordinatesArray[i]; const next = coordinatesArray[(i + 1) % coordinatesArray.length]; if (curr.x === next.x) { vLines.push({ x: curr.x, y1: Math.min(curr.y, next.y), y2: Math.max(curr.y, next.y) }); } else { hLines.push({ y: curr.y, x1: Math.min(curr.x, next.x), x2: Math.max(curr.x, next.x) }); } } // Check if a point is on the boundary function isOnBoundary(x, y) { for (const line of hLines) { if (y === line.y && x >= line.x1 && x <= line.x2) return true; } for (const line of vLines) { if (x === line.x && y >= line.y1 && y <= line.y2) return true; } return false; } // Check if a point is inside using ray casting (count vertical line crossings to the left) function isInside(x, y) { let crossings = 0; for (const line of vLines) { if (line.x < x && y >= line.y1 && y < line.y2) { crossings++; } } return crossings % 2 === 1; } // Check if a point is valid (on boundary or inside) function isValid(x, y) { return isOnBoundary(x, y) || isInside(x, y); } // Check if entire rectangle is valid function isRectValid(x1, y1, x2, y2) { // Check all corner and edge points at compressed coordinates within the rectangle for (const y of yCoords) { if (y < y1 || y > y2) continue; for (const x of xCoords) { if (x < x1 || x > x2) continue; if (!isValid(x, y)) return false; } } // Also check if any boundary line crosses through the rectangle's interior // creating an "outside" region for (const line of vLines) { if (line.x > x1 && line.x < x2) { // Vertical line inside rectangle - check if it creates a gap if (line.y1 > y1 || line.y2 < y2) { // Line doesn't span full height - might create invalid region if (line.y1 > y1 && !isValid(line.x, y1)) return false; if (line.y2 < y2 && !isValid(line.x, y2)) return false; } } } return true; } // Find largest valid rectangle with red corners let maxArea = 0; for (let i = 0; i < coordinatesArray.length; i++) { for (let j = i + 1; j < coordinatesArray.length; j++) { const r1 = coordinatesArray[i]; const r2 = coordinatesArray[j]; const x1 = Math.min(r1.x, r2.x); const x2 = Math.max(r1.x, r2.x); const y1 = Math.min(r1.y, r2.y); const y2 = Math.max(r1.y, r2.y); if (isRectValid(x1, y1, x2, y2)) { const area = (x2 - x1 + 1) * (y2 - y1 + 1); if (area > maxArea) maxArea = area; } } } console.timeEnd("part2"); console.log(maxArea); // functions