mirror of
https://github.com/parnic/advent-of-code-2024.git
synced 2025-06-16 12:30:13 -05:00
Day 12
This one is sloppier than I'd like, but these visual problems always throw me for a loop anyway. I was lucky I was able to use the ugly "num corners" trick quickly here.
This commit is contained in:
210
src/12.cs
Normal file
210
src/12.cs
Normal file
@ -0,0 +1,210 @@
|
||||
using aoc2024.Util;
|
||||
|
||||
namespace aoc2024;
|
||||
|
||||
internal class Day12 : Day
|
||||
{
|
||||
private char[][] grid = [];
|
||||
|
||||
internal override void Parse()
|
||||
{
|
||||
var lines = Util.Parsing.ReadAllLines($"{GetDay()}").ToList();
|
||||
grid = new char[lines.Count][];
|
||||
for (int y = 0; y < lines.Count; y++)
|
||||
{
|
||||
var row = new char[lines[y].Length];
|
||||
for (int x = 0; x < lines[y].Length; x++)
|
||||
{
|
||||
row[x] = lines[y][x];
|
||||
}
|
||||
grid[y] = row;
|
||||
}
|
||||
}
|
||||
|
||||
internal override string Part1()
|
||||
{
|
||||
HashSet<ivec2> used = [];
|
||||
HashSet<(char, HashSet<ivec2>)> plots = [];
|
||||
for (int y = 0; y < grid.Length; y++)
|
||||
{
|
||||
for (int x = 0; x < grid[y].Length; x++)
|
||||
{
|
||||
var v = new ivec2(x, y);
|
||||
if (used.Contains(v))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var plot = (grid[y][x], new HashSet<ivec2>());
|
||||
plots.Add(plot);
|
||||
var q = new Queue<ivec2>();
|
||||
q.Enqueue(v);
|
||||
plot.Item2.Add(v);
|
||||
while (q.Count > 0)
|
||||
{
|
||||
var check = q.Dequeue();
|
||||
var neighbors = check.GetBoundedOrthogonalNeighbors(0, 0, grid[0].Length - 1, grid.Length - 1);
|
||||
foreach (var n in neighbors)
|
||||
{
|
||||
if (grid[n.y][n.x] == grid[y][x] && !plot.Item2.Contains(n))
|
||||
{
|
||||
q.Enqueue(n);
|
||||
plot.Item2.Add(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
used.UnionWith(plot.Item2);
|
||||
}
|
||||
}
|
||||
|
||||
long total = 0;
|
||||
foreach (var plot in plots)
|
||||
{
|
||||
var area = plot.Item2.Count;
|
||||
var perimeter = 0;
|
||||
foreach (var plotPt in plot.Item2)
|
||||
{
|
||||
perimeter += plotPt.GetOrthogonalNeighbors().Count(pt => !plot.Item2.Contains(pt));
|
||||
}
|
||||
|
||||
var plotTotal = area * perimeter;
|
||||
total += plotTotal;
|
||||
}
|
||||
|
||||
return $"Area * perimeter cost: <+white>{total}";
|
||||
}
|
||||
|
||||
private bool IsSame(char c, ivec2 pt)
|
||||
{
|
||||
if (!pt.IsWithinRange(0, 0, grid[0].Length - 1, grid.Length - 1))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return grid[pt.y][pt.x] == c;
|
||||
}
|
||||
|
||||
private int NumCorners(IEnumerable<ivec2> points)
|
||||
{
|
||||
int numCorners = 0;
|
||||
foreach (var pt in points)
|
||||
{
|
||||
var ch = grid[pt.y][pt.x];
|
||||
// convex
|
||||
{
|
||||
// top-left
|
||||
if (!IsSame(ch, pt - new ivec2(1, 0)) &&
|
||||
!IsSame(ch, pt - new ivec2(0, 1)))
|
||||
{
|
||||
numCorners++;
|
||||
}
|
||||
|
||||
// top-right
|
||||
if (!IsSame(ch, pt + new ivec2(1, 0)) &&
|
||||
!IsSame(ch, pt - new ivec2(0, 1)))
|
||||
{
|
||||
numCorners++;
|
||||
}
|
||||
|
||||
// bottom-left
|
||||
if (!IsSame(ch, pt - new ivec2(1, 0)) &&
|
||||
!IsSame(ch, pt + new ivec2(0, 1)))
|
||||
{
|
||||
numCorners++;
|
||||
}
|
||||
|
||||
// bottom-right
|
||||
if (!IsSame(ch, pt + new ivec2(1, 0)) &&
|
||||
!IsSame(ch, pt + new ivec2(0, 1)))
|
||||
{
|
||||
numCorners++;
|
||||
}
|
||||
}
|
||||
// concave
|
||||
{
|
||||
// top-left
|
||||
if (IsSame(ch, pt - new ivec2(1, 0)) &&
|
||||
IsSame(ch, pt - new ivec2(0, 1)) &&
|
||||
!IsSame(ch, pt - ivec2.ONE))
|
||||
{
|
||||
numCorners++;
|
||||
}
|
||||
// top-right
|
||||
if (IsSame(ch, pt + new ivec2(1, 0)) &&
|
||||
IsSame(ch, pt - new ivec2(0, 1)) &&
|
||||
!IsSame(ch, pt + new ivec2(1, -1)))
|
||||
{
|
||||
numCorners++;
|
||||
}
|
||||
// bottom-left
|
||||
if (IsSame(ch, pt - new ivec2(1, 0)) &&
|
||||
IsSame(ch, pt + new ivec2(0, 1)) &&
|
||||
!IsSame(ch, pt + new ivec2(-1, 1)))
|
||||
{
|
||||
numCorners++;
|
||||
}
|
||||
// bottom-right
|
||||
if (IsSame(ch, pt + new ivec2(1, 0)) &&
|
||||
IsSame(ch, pt + new ivec2(0, 1)) &&
|
||||
!IsSame(ch, pt + new ivec2(1, 1)))
|
||||
{
|
||||
numCorners++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return numCorners;
|
||||
}
|
||||
|
||||
internal override string Part2()
|
||||
{
|
||||
HashSet<ivec2> used = [];
|
||||
List<(char, HashSet<ivec2>)> plots = [];
|
||||
for (int y = 0; y < grid.Length; y++)
|
||||
{
|
||||
for (int x = 0; x < grid[y].Length; x++)
|
||||
{
|
||||
var v = new ivec2(x, y);
|
||||
if (used.Contains(v))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var plot = (grid[y][x], new HashSet<ivec2>());
|
||||
plots.Add(plot);
|
||||
var q = new Queue<ivec2>();
|
||||
q.Enqueue(v);
|
||||
plot.Item2.Add(v);
|
||||
while (q.Count > 0)
|
||||
{
|
||||
var check = q.Dequeue();
|
||||
var neighbors = check.GetBoundedOrthogonalNeighbors(0, 0, grid[0].Length - 1, grid.Length - 1);
|
||||
foreach (var n in neighbors)
|
||||
{
|
||||
if (grid[n.y][n.x] != grid[y][x] || plot.Item2.Contains(n))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
q.Enqueue(n);
|
||||
plot.Item2.Add(n);
|
||||
}
|
||||
}
|
||||
|
||||
used.UnionWith(plot.Item2);
|
||||
}
|
||||
}
|
||||
|
||||
long total = 0;
|
||||
foreach (var plot in plots)
|
||||
{
|
||||
var area = plot.Item2.Count;
|
||||
var numSides = NumCorners(plot.Item2);
|
||||
|
||||
total += area * numSides;
|
||||
}
|
||||
|
||||
return $"Area * sides cost: <+white>{total}";
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user