Day 12: Garden Groups
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
- What is this?: Here is a post with a large amount of details: https://programming.dev/post/6637268
- Where do I participate?: https://adventofcode.com/
- Is there a leaderboard for the community?: We have a programming.dev leaderboard with the info on how to join in this post: https://programming.dev/post/6631465
C#
There is probably a more efficient way of finding the sides, but this way was what came to me.
using System.Diagnostics; using Common; namespace Day12; static class Program { static void Main() { var start = Stopwatch.GetTimestamp(); var sampleInput = Input.ParseInput("sample.txt"); var programInput = Input.ParseInput("input.txt"); var (samplePart1, samplePart2) = Solve(sampleInput); Console.WriteLine($"Part 1 sample: {samplePart1}"); Console.WriteLine($"Part 1 input: {samplePart2}"); var (inputPart1, inputPart2) = Solve(programInput); Console.WriteLine($"Part 2 sample: {inputPart1}"); Console.WriteLine($"Part 2 input: {inputPart2}"); Console.WriteLine($"That took about {Stopwatch.GetElapsedTime(start)}"); } static (int part1, int part2) Solve(Input i) { var retail = 0; var bulk = 0; var allPlotPoints = new Dictionary<char, HashSet<Point>>(); foreach (var p in Grid.EnumerateAllPoints(i.Bounds)) { var plant = i.ElementAt(p); if (!allPlotPoints.TryGetValue(plant, out var previousPlotPoints)) { previousPlotPoints = new(); allPlotPoints[plant] = previousPlotPoints; } else if (previousPlotPoints.Contains(p)) continue; var plotPoints = new HashSet<Point>(); var perimeter = SearchPlot(i, plotPoints, plant, p); var area = plotPoints.Count; var sides = CountSides(plotPoints); retail += area * perimeter; bulk += area * sides; previousPlotPoints.AddRange(plotPoints); } return (retail, bulk); } static int CountSides(HashSet<Point> plot) { var sides = 0; // Track the points we've visited searching for sides HashSet<Point> visitedDownRight = new(), visitedDownLeft = new(), visitedRightDown = new(), visitedRightUp = new(); // Sort the points in the plot from upper-left to lower-right, so we can // go through them in reading order foreach (var p in plot.OrderBy(p => (p.Row * 10000) + p.Col)) { // Move right while looking up sides += GetSideLength(p, plot, visitedRightUp, Direction.Right, Direction.Up) > 0 ? 1 : 0; // Move right while looking down sides += GetSideLength(p, plot, visitedRightDown, Direction.Right, Direction.Down) > 0 ? 1 : 0; // Move down while looking right sides += GetSideLength(p, plot, visitedDownRight, Direction.Down, Direction.Right) > 0 ? 1 : 0; // Move down while looking left sides += GetSideLength(p, plot, visitedDownLeft, Direction.Down, Direction.Left) > 0 ? 1 : 0; } return sides; } static int GetSideLength(Point p, HashSet<Point> plotPoints, HashSet<Point> visited, Direction move, Direction look) { if (!plotPoints.Contains(p)) return 0; if (!visited.Add(p)) return 0; if (plotPoints.Contains(p.Move(look))) return 0; return 1 + GetSideLength(p.Move(move), plotPoints, visited, move, look); } static int SearchPlot(Input i, HashSet<Point> plotPoints, char plant, Point p) { if (!plotPoints.Add(p)) return 0; return p .GetCardinalMoves() .Select(move => { if (!i.IsInBounds(move) || (i.ElementAt(move) != plant)) return 1; return SearchPlot(i, plotPoints, plant, move); }) .Sum(); } } public class Input { public required string[] Map { get; init; } public Point Bounds => new Point(this.Map.Length, this.Map[0].Length); public char ElementAt(Point p) => this.Map[p.Row][p.Col]; public bool IsInBounds(Point p) => p.IsInBounds(this.Map.Length, this.Map[0].Length); public static Input ParseInput(string file) => new Input() { Map = File.ReadAllLines(file), }; }
What is the
Point
type? I’m surprised that you can’t just lexicographically sort instead ofplot.OrderBy(p => (p.Row * 10000) + p.Col)
.It’s a simple record type I use for (x,y) coordinate problems:
record struct Point(int X, int Y);
It’s defined in a separate project containing things I use in multiple problems.
Maybe I could have done it that way, but this was the first thing I thought of, and it worked :)