01020304050607080910111213141516171819202122232425

Advent of Code

2023/14

Parabolic Reflector Dish

in C#

by encse

The task description is copyrighted, but it's available here.

We are playing Boulder Dash today, but instead of moving a character on the screen we tilt the screen itself and move all the boulders at once. The task asks us to implement tilting in each directions (North, South, East and West), but I just implemented North and kept rotating the board by 90 degrees.

Then we start tilting like crazy - four billion times. When you see iterate for <a big number> you immediately start looking for some repetition. This is not different with today's problem either. In just about a hundred steps the board enters into a loop, so we can jump over the rest of the tilting work and just read the result out from the list of states we collected.

namespace AdventOfCode.Y2023.Day14;

using System;
using System.Collections.Generic;
using System.Linq;
using Map = char[][];

[ProblemName("Parabolic Reflector Dish")]
class Solution : Solver {

    public object PartOne(string input) => 
        Measure(Tilt(Parse(input)));

    public object PartTwo(string input) => 
        Measure(Iterate(Parse(input), Cycle, 1_000_000_000));

    Map Parse(string input) => (
        from l in input.Split('\n') select l.ToCharArray()
    ).ToArray();

    int Crow(char[][] map) => map.Length;
    int Ccol(char[][] map) => map[0].Length;

    Map Iterate(Map map, Func<Map, Map> cycle, int count) {
        // The usual trick: keep iterating until we find a loop, make a shortcut
        // and read the result from the accumulated history.
        var history = new List<string>();
        while (count > 0) {
            map = cycle(map);
            count--;

            var mapString = string.Join("\n", map.Select(l=> new string(l)));
            var idx = history.IndexOf(mapString);
            if (idx < 0) {
                history.Add(mapString);
            } else {
                var loopLength = history.Count - idx;
                var remainder = count % loopLength; 
                return Parse(history[idx + remainder]);
            }
        }
        return map;
    }

    Map Cycle(Map map) {
        for (var i = 0; i < 4; i++) {
            map = Rotate(Tilt(map));
        }
        return map;
    }

    // Tilt the map to the North, so that the 'O' tiles roll to the top.
    Map Tilt(Map map) {
        for (var icol = 0; icol < Ccol(map); icol++) {
            var irowT = 0; // tells where to roll up the next 'O' tile
            for (var irowS = 0; irowS < Crow(map); irowS++) {
                if (map[irowS][icol] == '#') {
                    irowT = irowS + 1;
                } else if (map[irowS][icol] == 'O') {
                    map[irowS][icol] = '.'; 
                    map[irowT][icol] = 'O'; 
                    irowT++;
                }
            }
        }
        return map;
    }

    // Ugly coordinate magic, turns the map 90º clockwise
    Map Rotate(Map src) {
        var dst = new char[Crow(src)][];
        for (var irow = 0; irow < Ccol(src); irow++) {
            dst[irow] = new char[Ccol(src)];
            for (var icol = 0; icol < Crow(src); icol++) {
                dst[irow][icol] = src[Crow(src) - icol - 1][irow];
            }
        }
        return dst;
    }

    // returns the cummulated distances of 'O' tiles from the bottom of the map
    int Measure(Map map) =>  
        map.Select((row, irow) => 
            (Crow(map) - irow) * row.Count(ch => ch == 'O')
        ).Sum();
}

Please ☆ my repo if you like it!

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