01020304050607080910111213141516171819202122232425

Advent of Code

2024/10

Hoof It

in C#

by encse

You all arrive at a Lava Production Facility on a floating island in the sky. As the others begin to search the massive industrial complex, you feel a small nose boop your leg and look down to discover a reindeer wearing a hard hat.

The reindeer is holding a book titled "Lava Island Hiking Guide". However, when you open the book, you discover that most of it seems to have been scorched by lava! As you're about to ask how you can help, the reindeer brings you a blank topographic map of the surrounding area (your puzzle input) and looks up at you excitedly.

Visit the website for the full story and puzzle description.

Today's problem is surprisingly straightforward compared to yesterday's pointer juggling. We finally get to use our favorite queue data structure to implement a flood fill. I saw this coming...

As usual, we use a dictionary with complex numbers to parse the input. The meat of the solution is in GetTrailsFrom, which returns all trails starting at a specific trailhead.

The difference between Part 1 and Part 2 lies in how distinct trails are defined. I decided to return all trails keyed by their trailheads in GetAllTrails and delegate the distinctness logic to PartOne and PartTwo.

A nice and easy task for today!

namespace AdventOfCode.Y2024.Day10;

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

using Map = System.Collections.Immutable.ImmutableDictionary<System.Numerics.Complex, char>;

[ProblemName("Hoof It")]
class Solution : Solver {

    Complex Up = Complex.ImaginaryOne;
    Complex Down = -Complex.ImaginaryOne;
    Complex Left = -1;
    Complex Right = 1;

    public object PartOne(string input) => GetAllTrails(input).Sum(t => t.Value.Distinct().Count());
    public object PartTwo(string input) => GetAllTrails(input).Sum(t => t.Value.Count());

    Dictionary<Complex, List<Complex>> GetAllTrails(string input) {
        var map = GetMap(input);
        return GetTrailHeads(map).ToDictionary(t => t, t => GetTrailsFrom(map, t));
    }

    IEnumerable<Complex> GetTrailHeads(Map map) => map.Keys.Where(pos => map[pos] == '0');

    List<Complex> GetTrailsFrom(Map map, Complex trailHead) {
        // standard floodfill algorithm using a queue
        var positions = new Queue<Complex>();
        positions.Enqueue(trailHead);
        var trails = new List<Complex>();
        while (positions.Any()) {
            var point = positions.Dequeue();
            if (map[point] == '9') {
                trails.Add(point);
            } else {
                foreach (var dir in new[] { Up, Down, Left, Right }) {
                    if (map.GetValueOrDefault(point + dir) == map[point] + 1) {
                        positions.Enqueue(point + dir);
                    }
                }
            }
        }
        return trails;
    }

    // store the points in a dictionary so that we can iterate over them and 
    // to easily deal with points outside the area using GetValueOrDefault
    Map GetMap(string input) {
        var map = input.Split("\n");
        return (
            from y in Enumerable.Range(0, map.Length)
            from x in Enumerable.Range(0, map[0].Length)
            select new KeyValuePair<Complex, char>(x + y * Down, map[y][x])
        ).ToImmutableDictionary();
    }
}

Please ☆ my repo if you like it!

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