01020304050607080910111213141516171819202122232425

Advent of Code

2023/18

Lavaduct Lagoon

in C#

by encse

If you are not familiar with the problem, you can read it here.

Both parts ask for the integer area covered by some polygon. But it wouldn't be Advent of Code, if the polygon came in the form of coordinates. First we are dealing with some odd dig instruction list with hex colors.

The polygon in Part 1 is much smaller and one can use any algorithm from flood fill to ray casting, but Part 2 makes it clear that we need to pull out the bigger guns.

I heard about the Shoelace formula, but haven't used it in practice yet. I knew that I can calculate the (signed) area of a polygon by summing up some determinants using the neighbouring vertices. But I haven't heard about Pick's theorem before, so it made me think for a while to extend this idea to return the integer area instead of the real one.

Having solved Part 1 I could somehow guess the right formula involving half the boundary plus 1, which sounds just right and was enough for Part 2, then still being puzzled I went to the solution thread and read about Pick.

Pick's theorem connects the area returned by the Shoelace formula with the number of interior points and points in the boundary. The problem asked for the sum of these.

I give myself an extra ⭐ for learning something today.

namespace AdventOfCode.Y2023.Day18;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;

[ProblemName("Lavaduct Lagoon")]
class Solution : Solver {

    public object PartOne(string input) => Area(Steps1(input));
    public object PartTwo(string input) => Area(Steps2(input));

    IEnumerable<Complex> Steps1(string input) =>
        from line in input.Split('\n')
        let parts = line.Split(' ')
        let dir = parts[0] switch {
            "R" => Complex.One,
            "U" => -Complex.ImaginaryOne,
            "L" => -Complex.One,
            "D" => Complex.ImaginaryOne,
            _ => throw new Exception()
        }
        let dist = int.Parse(parts[1])
        select dir * dist;

    IEnumerable<Complex> Steps2(string input) =>
        from line in input.Split('\n')
        let hex = line.Split(' ')[2]
        let dir = hex[7] switch {
            '0' => Complex.One,
            '1' => -Complex.ImaginaryOne,
            '2' => -Complex.One,
            '3' => Complex.ImaginaryOne,
            _ => throw new Exception()
        }
        let dist = Convert.ToInt32(hex[2..7], 16)
        select dir * dist;

    // We are using a combination of the shoelace formula with Pick's theorem
    double Area(IEnumerable<Complex> steps) {
        var vertices = Vertices(steps).ToList();

        // Shoelace formula https://en.wikipedia.org/wiki/Shoelace_formula
        var shiftedVertices = vertices.Skip(1).Append(vertices[0]);
        var shoelaces =
            from points in vertices.Zip(shiftedVertices)
            let p1 = points.First
            let p2 = points.Second
            select p1.Real * p2.Imaginary - p1.Imaginary * p2.Real;
        var area = Math.Abs(shoelaces.Sum()) / 2;

        // Pick's theorem  https://en.wikipedia.org/wiki/Pick%27s_theorem
        var boundary = steps.Select(x => x.Magnitude).Sum();
        var interior = area - boundary / 2 + 1;

        // Integer area
        return boundary + interior;
    }

    IEnumerable<Complex> Vertices(IEnumerable<Complex> steps) {
        var pos = Complex.Zero;
        foreach (var step in steps) {
            pos += step;
            yield return pos;
        }
    }
}

Please ☆ my repo if you like it!

© 2025 Advent of Code is a registered trademark in the US Images provided by Bing image creator