Day 4: Ceres Search

Megathread guidelines

  • Keep top level comments as only solutions, if you want to say something other than a solution put it in a new post. (replies to comments can be whatever)
  • You can send code in code blocks by using three backticks, the code, and then three backticks or use something such as https://topaz.github.io/paste/ if you prefer sending it through a URL

FAQ

  • SteveDinn
    link
    fedilink
    English
    arrow-up
    1
    ·
    12 hours ago

    C#

    namespace Day04;
    
    static class Program
    {
        public record struct Point(int Row, int Col);
    
        static void Main(string[] args)
        {
            var sample = File.ReadAllLines("sample.txt");
            var data = File.ReadAllLines("data.txt");
    
            Console.WriteLine($"Part 1 (sample): {SolvePart1(sample)}");
            Console.WriteLine($"Part 1 (data): {SolvePart1(data)}");
    
            Console.WriteLine($"Part 2 (sample): {SolvePart2(sample)}");
            Console.WriteLine($"Part 2 (data): {SolvePart2(data)}");
        }
    
        private static readonly string Search = "XMAS";
    
        private static readonly Func<Point, Point>[] DirectionalMoves =
        {
            p => new Point(p.Row + 1, p.Col),
            p => new Point(p.Row + 1, p.Col + 1),
            p => new Point(p.Row, p.Col + 1),
            p => new Point(p.Row - 1, p.Col + 1),
            p => new Point(p.Row - 1, p.Col),
            p => new Point(p.Row - 1, p.Col - 1),
            p => new Point(p.Row, p.Col - 1),
            p => new Point(p.Row + 1, p.Col - 1),
        };
    
        private static readonly Func<Point, Point>[] ForwardSlashMoves =
        {
            p => new Point(p.Row - 1, p.Col - 1),
            p => new Point(p.Row + 1, p.Col + 1),
        };
        
        private static readonly Func<Point, Point>[] BackSlashMoves =
        {
            p => new Point(p.Row + 1, p.Col - 1),
            p => new Point(p.Row - 1, p.Col + 1),
        };
    
        static long SolvePart1(string[] data)
        {
            return Enumerable
                .Range(0, data.Length)
                .SelectMany(row => Enumerable.Range(0, data[row].Length)
                    .Select(col => new Point(row, col)))
                .Where(p => IsMatch(data, p, Search[0]))
                .Sum(p => DirectionalMoves
                    .Count(move => DeepMatch(data, move(p), move, Search, 1)));
        }
    
        static long SolvePart2(string[] data)
        {
            return Enumerable
                .Range(0, data.Length)
                .SelectMany(row => Enumerable.Range(0, data[row].Length)
                    .Select(col => new Point(row, col)))
                .Where(p => IsMatch(data, p, 'A'))
                .Count(p => CheckDiagonalMoves(data, p, ForwardSlashMoves)
                            && CheckDiagonalMoves(data, p, BackSlashMoves));
        }
    
        static bool CheckDiagonalMoves(string[] data, Point p, Func<Point, Point>[] moves)
            => (IsMatch(data, moves[0](p), 'S') && IsMatch(data, moves[1](p), 'M'))
               || (IsMatch(data, moves[0](p), 'M') && IsMatch(data, moves[1](p), 'S'));
    
        static bool DeepMatch(string[] data, Point p, Func<Point, Point> move, string search, int searchIndex) =>
            (searchIndex >= search.Length) ? true :
            (!IsMatch(data, p, search[searchIndex])) ? false :
            DeepMatch(data, move(p), move, search, searchIndex + 1);
    
        static bool IsMatch(string[] data, Point p, char searchChar)
            => IsInBounds(data, p) && (data[p.Row][p.Col] == searchChar);
    
        static bool IsInBounds(string[] data, Point p) =>
            (p.Row >= 0) && (p.Col >= 0) && (p.Row < data.Length) && (p.Col < data[0].Length);
    }