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
You are viewing a single thread.
View all comments 3 points
C#
public class Day12 : Solver
{
private string[] data;
private int width, height;
private Dictionary<int, long> perimeters = [];
private Dictionary<int, long> areas = [];
private Dictionary<int, long> sides = [];
private int region_count;
public void Presolve(string input) {
data = input.Trim().Split("\n").ToArray();
height = data.Length;
width = data[0].Length;
var graph_cc = MakeGraph(false);
var cc = new ConnectedComponentsAlgorithm<Point, PointEdge>(graph_cc);
cc.Compute();
var graph_all = MakeGraph(true);
Dictionary<(int Component, int Y), List<int>> x_sides = [];
Dictionary<(int Component, int X), List<int>> y_sides = [];
var search = new UndirectedBreadthFirstSearchAlgorithm<Point, PointEdge>(graph_all);
search.SetRootVertex((0, 0));
search.FinishVertex += vertex => {
if (IsWithinBounds(vertex.Item1, vertex.Item2)) {
int component = cc.Components[vertex];
areas.TryAdd(component, 0L);
areas[component] += 1;
}
};
search.ExamineEdge += edge => {
var (si, ti) = (IsWithinBounds(edge.Source), IsWithinBounds(edge.Target));
bool border = si != ti || cc.Components[edge.Source] != cc.Components[edge.Target];
if (si && border) {
int component = cc.Components[edge.Source];
perimeters.TryAdd(component, 0L);
perimeters[component] += 1;
if (edge.Source.Item1 == edge.Target.Item1) {
int y = Math.Min(edge.Source.Item2, edge.Target.Item2);
x_sides.TryAdd((component, y), []);
x_sides[(component, y)].Add(edge.Source.Item2 > edge.Target.Item2 ? edge.Source.Item1 : -edge.Source.Item1 - 5);
} else {
int x = Math.Min(edge.Source.Item1, edge.Target.Item1);
y_sides.TryAdd((component, x), []);
y_sides[(component, x)].Add(edge.Source.Item1 > edge.Target.Item1 ? edge.Source.Item2 : -edge.Source.Item2 - 5);
}
}
};
search.Compute();
region_count = cc.ComponentCount;
foreach (var side_projection in x_sides) {
side_projection.Value.Sort();
sides.TryAdd(side_projection.Key.Component, 0);
int last_x = int.MinValue;
foreach (var x in side_projection.Value) {
if (x != (last_x + 1)) sides[side_projection.Key.Component] += 1;
last_x = x;
}
}
foreach (var side_projection in y_sides) {
side_projection.Value.Sort();
sides.TryAdd(side_projection.Key.Component, 0);
int last_y = int.MinValue;
foreach (var y in side_projection.Value) {
if (y != (last_y + 1)) sides[side_projection.Key.Component] += 1;
last_y = y;
}
}
foreach (var component in Enumerable.Range(0, region_count)) {
if (!areas.ContainsKey(component)) continue;
}
}
public string SolveFirst() =>
Enumerable.Range(0, region_count)
.Where(component => areas.ContainsKey(component))
.Select(component => areas[component] * perimeters[component]).Sum().ToString();
public string SolveSecond() =>
Enumerable.Range(0, region_count)
.Where(component => areas.ContainsKey(component))
.Select(component => areas[component] * sides[component]).Sum().ToString();
private record struct PointEdge(Point Source, Point Target): IEdge<Point>;
private IUndirectedGraph<Point, PointEdge> MakeGraph(bool with_edges_between_plots)=>
new DelegateUndirectedGraph<Point, PointEdge>(GetVertices(), with_edges_between_plots? GetAllEdges : GetEdgesWithoutBorders, false);
private bool IsWithinBounds(int x, int y) => x >= 0 && x < width && y >= 0 && y < height;
private bool IsWithinBounds(Point p) => IsWithinBounds(p.Item1, p.Item2);
private readonly (int, int)[] directions = [(-1, 0), (0, -1), (1, 0), (0, 1)];
private bool GetEdgesWithoutBorders(Point arg, out IEnumerable<PointEdge> result) {
List<PointEdge> result_list = [];
var (x, y) = arg;
bool inside = IsWithinBounds(x, y);
foreach (var (dx, dy) in directions) {
var (ox, oy) = (x + dx, y + dy);
if (!inside || !IsWithinBounds(ox, oy)) continue;
if (data[y][x] == data[oy][ox]) result_list.Add(new(arg, (ox, oy)));
}
result = result_list;
return true;
}
private bool GetAllEdges(Point arg, out IEnumerable<PointEdge> result) {
List<PointEdge> result_list = [];
var (x, y) = arg;
foreach (var (dx, dy) in directions) {
var (ox, oy) = (x + dx, y + dy);
if (ox >= -1 && ox <= width && oy >= -1 && oy <= height) result_list.Add(new(arg, (ox, oy)));
}
result = result_list;
return true;
}
private IEnumerable<(int, int)> GetVertices() => Enumerable.Range(-1, width + 2).SelectMany(x => Enumerable.Range(-1, height + 2).Select(y => (x, y)));
}