From 66f0f805a8cdc4b762d8b09522ebc99c6d41e775 Mon Sep 17 00:00:00 2001 From: Parnic Date: Wed, 4 Dec 2024 08:45:08 -0600 Subject: [PATCH] Day 4 This solution feels pretty bad. I think I shouldn't have used my ivec2 utilities and just checked the next spots for part 2. It would probably have been simpler given all the constraints. I'm also certain there's a smarter way to solve this than what I did, but...it works, so...? --- src/04.cs | 150 +++++++++++++++++++++++++++++++++++++++++++++++ src/Util/Vec2.cs | 23 ++++++++ 2 files changed, 173 insertions(+) create mode 100644 src/04.cs diff --git a/src/04.cs b/src/04.cs new file mode 100644 index 0000000..a2ec8ee --- /dev/null +++ b/src/04.cs @@ -0,0 +1,150 @@ +using aoc2024.Util; + +namespace aoc2024; + +internal class Day04 : Day +{ + private char[][] puzzle = []; + internal override void Parse() + { + var lines = Util.Parsing.ReadAllLines($"{GetDay()}").ToList(); + int numRows = lines.Count; + puzzle = new char[numRows][]; + for (int i = 0; i < numRows; i++) + { + var row = lines[i].ToCharArray(); + puzzle[i] = row; + } + } + + private static bool IsValid(char[][] puzzle, long row, long col) + { + return row >= 0 && col >= 0 && row < puzzle.Length && col < puzzle[row].Length; + } + + private static int HasXmas(char[][] puzzle, long row, long col) + { + int numHas = 0; + const string lookingFor = "XMAS"; + var dirs = ivec2.EIGHTWAY; + foreach (var dir in dirs) + { + int onChar = 1; + var nextPos = new ivec2(row, col) + dir; + while (IsValid(puzzle, nextPos.x, nextPos.y) && puzzle[nextPos.x][nextPos.y] == lookingFor[onChar]) + { + onChar++; + nextPos += dir; + + if (onChar == lookingFor.Length) + { + numHas++; + break; + } + } + } + + return numHas; + } + + internal override string Part1() + { + int numFound = 0; + for (int row = 0; row < puzzle.Length; row++) + { + for (int col = 0; col < puzzle[row].Length; col++) + { + if (puzzle[row][col] == 'X') + { + numFound += HasXmas(puzzle, row, col); + } + } + } + + return $"Num XMAS: <+white>{numFound}"; + } + + private static bool HasMasInX(char[][] puzzle, long row, long col) + { + const int maxDist = 2; + if (!IsValid(puzzle, row + maxDist, col) || !IsValid(puzzle, row, col + maxDist) || !IsValid(puzzle, row + maxDist, col + maxDist)) + { + // not enough spots to search + return false; + } + + List ms = []; + for (int y = 0; y <= maxDist; y++) + { + if (y == 1) + { + continue; + } + + for (int x = 0; x <= maxDist; x++) + { + if (x == 1) + { + continue; + } + + if (puzzle[row + y][col + x] == 'M') + { + ms.Add(new ivec2(col + x, row + y)); + } + } + } + + if (ms.Count != 2) + { + return false; + } + + foreach (var m in ms) + { + var requiredDir = ivec2.ZERO; + if (m.y == row && m.x == col) + { + requiredDir = ivec2.DOWN + ivec2.RIGHT; + } + else if (m.y == row && m.x == col + maxDist) + { + requiredDir = ivec2.DOWN + ivec2.LEFT; + } + else if (m.y == row + maxDist && m.x == col) + { + requiredDir = ivec2.UP + ivec2.RIGHT; + } + else if (m.y == row + maxDist && m.x == col + maxDist) + { + requiredDir = ivec2.UP + ivec2.LEFT; + } + + var next1 = m + requiredDir; + var next2 = m + requiredDir + requiredDir; + if (puzzle[next1.y][next1.x] != 'A' || puzzle[next2.y][next2.x] != 'S') + { + return false; + } + } + + return true; + } + + internal override string Part2() + { + int numFound = 0; + for (int row = 0; row < puzzle.Length; row++) + { + for (int col = 0; col < puzzle[row].Length; col++) + { + if (HasMasInX(puzzle, row, col)) + { + numFound++; + } + } + } + + return $"Num X-MAS: <+white>{numFound}"; + } +} diff --git a/src/Util/Vec2.cs b/src/Util/Vec2.cs index 19421c9..4e56014 100644 --- a/src/Util/Vec2.cs +++ b/src/Util/Vec2.cs @@ -14,6 +14,7 @@ public readonly struct ivec2 : IEquatable, IComparable, IComparabl public static readonly ivec2 DOWN = new ivec2(0, 1); public static readonly ivec2[] FOURWAY = {RIGHT, LEFT, UP, DOWN}; public static readonly ivec2[] EIGHTWAY = {UP, UP + RIGHT, RIGHT, RIGHT + DOWN, DOWN, DOWN + LEFT, LEFT, LEFT + UP}; + public static readonly ivec2[] DIAGONALS = {UP + RIGHT, DOWN + RIGHT, UP + LEFT, DOWN + LEFT}; public ivec2(long xv, long yv) { @@ -100,6 +101,28 @@ public readonly struct ivec2 : IEquatable, IComparable, IComparabl } } + public IEnumerable GetDiagonalNeighbors(ivec2 p) + { + foreach (var dir in DIAGONALS) + { + yield return this + dir; + } + } + + public IEnumerable GetBoundedDiagonalNeighbors(int minX, int minY, int maxX, int maxY) + { + foreach (var dir in DIAGONALS) + { + var pt = this + dir; + if (!pt.IsWithinRange(minX, minY, maxX, maxY)) + { + continue; + } + + yield return pt; + } + } + public long this[long i] => (i == 0) ? x : y; public static ivec2 operator +(ivec2 v) => v;