-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDay12.cs
137 lines (131 loc) · 5.26 KB
/
Day12.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
using System.Collections.Generic;
using System.Linq;
using AdventOfCode2024.Util;
namespace AdventOfCode2024
{
internal class Day12(string input) : Solution(input)
{
readonly Grid<char> grid = new(input);
public override string Part1()
{
return Solve(false);
}
public override string Part2()
{
return Solve(true);
}
private string Solve(bool bulkDiscount)
{
int total = 0;
HashSet<GridCell<char>> unexplored = new(grid.GetFlattened());
while (unexplored.Count > 0)
{
// find the region for the first unexplored grid item
var regionBase = unexplored.First();
// this will be either perimeter or edges depending on bulk discount
int regionOutside = 0;
HashSet<GridCell<char>> region = [regionBase];
Queue<GridCell<char>> frontier = [];
frontier.Enqueue(regionBase);
// Expand region as far as possible
while (frontier.Count > 0)
{
var current = frontier.Dequeue();
var neighbours = current.GetNeighbours(false, true);
int perimeter = 0;
// explore neighbours
foreach (var neighbour in neighbours)
{
// check if neighbour is in region
if (neighbour != null && neighbour.Value == current.Value)
{
// only add to region/frontier if it isn't already in region
if (region.Contains(neighbour))
{
continue;
}
region.Add(neighbour);
frontier.Enqueue(neighbour);
}
else
{
// any out-of-region neighbours increase this cell's perimeter
perimeter++;
}
}
// calculate sides
if (bulkDiscount)
{
// single cell surrounded by other regions, 4 sides
if (perimeter == 4)
{
regionOutside = 4;
}
/* single cell surrounded by 3 other regions
* --+
* AA|
* --+
* 1 full side and 2 half sides = 2 sides
*/
else if (perimeter == 3)
{
regionOutside += 2;
}
// corner detection time!
// one corner = two half sides = 1 side
else
{
int[] offsets = [-1, 1];
int row = current.Row;
int col = current.Col;
char reg = current.Value;
foreach (int dr in offsets)
{
foreach (int dc in offsets)
{
// L-shape
if (
InRegion(row + dr, col, reg) && InRegion(row, col + dc, reg)
)
{
// Inside corner pattern
if (!InRegion(row + dr, col + dc, reg))
{
regionOutside++;
}
// Outside corner pattern
if (
!InRegion(row - dr, col, reg)
&& !InRegion(row, col - dc, reg)
)
{
regionOutside++;
}
}
}
}
}
}
// calculate perimeter
else
{
regionOutside += perimeter;
}
// this cell has now been explored
unexplored.Remove(current);
}
// at this point the whole region has been explored, add to total
total += region.Count * regionOutside;
}
return total.ToString();
}
private bool InRegion(int row, int col, char region)
{
if (grid.TryGetValue(row, col, out char value))
{
return value == region;
}
return false;
}
}
}