Largest Rectangle in a Polygon

446 words
3 min.
Post preview image
Advent of Code 2025: Day 9 – finding the largest axis-aligned rectangle inside a polygon

Day 9 is one of the more geometrically involved puzzles! 🔷 We’re given a list of 2D coordinates that form a polygon boundary:

7,1
11,1
11,7
9,7
9,5
2,5
2,3
7,3

Part 1

Part 1 asks for the largest axis-aligned rectangle whose corners are any two of the input coordinates. This is a brute-force O(n²) search – try every pair of points as opposite corners of a rectangle:

function findAreaOfLargestRectangle(coords: Coord[]): number {
    let maxArea = 0;

    coords.forEach((a) => {
        coords.forEach((b) => {
            if (!a.equals(b)) {
                const area = findArea(a, b);
                if (area > maxArea) maxArea = area;
            }
        });
    });

    return maxArea;
}

function findArea(a: Coord, b: Coord): number {
    const sideA = Math.abs(a.x - b.x) + 1;
    const sideB = Math.abs(a.y - b.y) + 1;
    return sideA * sideB;
}

Simple and effective for Part 1.


Part 2

Part 2 requires the largest rectangle fully contained inside the polygon. This is significantly harder and needs three key building blocks:

Step 1 – Build the polygon’s edges (vertical and horizontal segments connecting consecutive coordinate pairs).

Step 2 – Coordinate compression. We only need to check rectangles whose corners align with the polygon’s own x and y values, so we build compressed index maps.

Step 3 – Build a 2D prefix sum where each cell’s weight is the area it contributes inside the polygon. We determine inside/outside using ray casting along vertical edges.

function findAreaOfLargestRectanglePart2(coords: Coord[]): number {
    const edges = buildEdges(coords);
    const {xs, ys, xIndex, yIndex} = buildCompressedAxes(coords);
    const prefix = buildInsidePrefix(edges, xs, ys);

    let maxArea = 0;

    for (let i = 0; i < coords.length; i++) {
        const a = coords[i];
        for (let j = i + 1; j < coords.length; j++) {
            const b = coords[j];
            if (a.equals(b)) continue;

            const loX = Math.min(a.x, b.x), hiX = Math.max(a.x, b.x);
            const loY = Math.min(a.y, b.y), hiY = Math.max(a.y, b.y);
            const area = (hiX - loX + 1) * (hiY - loY + 1);
            if (area <= maxArea) continue;

            const x0 = xIndex.get(loX), x1 = xIndex.get(hiX + 1);
            const y0 = yIndex.get(loY), y1 = yIndex.get(hiY + 1);
            if (x0 === undefined || x1 === undefined || y0 === undefined || y1 === undefined) continue;

            const insideSum =
                prefix[y1][x1] - prefix[y0][x1] -
                prefix[y1][x0] + prefix[y0][x0];

            if (insideSum === area) maxArea = area;
        }
    }

    return maxArea;
}

The prefix sum query tells us how many cells of the candidate rectangle lie inside the polygon. If insideSum === area, the whole rectangle is interior.


3D Circuit Boxes and Union-Find
Bit-Flip Machines and Joltage Equations