-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDay12.cs
More file actions
173 lines (152 loc) · 5.38 KB
/
Day12.cs
File metadata and controls
173 lines (152 loc) · 5.38 KB
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Immutable;
using System.Linq.Expressions;
using _2024.Utils;
using System.Threading.Tasks;
namespace _2024._12;
public class Day12 : Base
{
public Day12(bool example) : base(example)
{
Day = "12";
}
private string[] _input = [];
private readonly HashSet<ValueTuple<int, int>> _areaPositions = [];
// theoretically it is possible to count the perimeter for part1 here as well (just count in the forloop before
// continue)
private void FindArea(int row, int col, char soil, HashSet<ValueTuple<int, int>> visited)
{
if (!visited.Add((row, col)))
{
return;
}
if (_input[row][col] == soil)
{
_areaPositions.Add((row, col));
}
foreach ((int dRow, int dCol) in new[] { (0, 1), (0, -1), (1, 0), (-1, 0) })
{
(int newRow, int newCol) = (row+dRow, col+dCol);
if (_input.IsOob((newRow, newCol)))
{
continue;
}
if (_input[newRow][newCol] != soil)
{
continue;
}
FindArea(newRow, newCol, soil, visited);
}
}
private int GetPerimeter(char soil, int stage)
{
int perimeter = 0;
foreach ((int row, int col) in _areaPositions)
{
int adjacentSides = 0;
// count how many adjacent fields are of the same soil type
foreach ((int dRow, int dCol) in new[] { (0, 1), (0, -1), (1, 0), (-1, 0) })
{
int newRow = row + dRow, newCol = col + dCol;
if (_input.IsOob((newRow, newCol)) || _input[newRow][newCol] != soil)
{
if (stage == 1)
{
perimeter++;
}
continue;
}
if (_input[newRow][newCol] == soil)
{
adjacentSides++;
}
}
// stage 1 doesnt count sides, but actual perimeter
if (stage == 1)
{
continue;
}
// count corners
switch (adjacentSides)
{
case 0:
// handles single "island" case
perimeter += 4;
continue;
case 1:
// such a case: aa
perimeter += 2;
continue;
}
// checks these four shapes:
// aa b c cc
// a bb cc c
// any of those would signal one corner
foreach((int dRow, int dCol) in new [] {(-1, 1), (-1, -1), (1, 1), (1, -1)}){
int newRow = row + dRow, newCol = col + dCol;
// this is not a valid corner
if (_input.IsOob((newRow, newCol)) || _input[row][newCol] != soil || _input[newRow][col] != soil)
{
continue;
}
// both positions need to be either oob or not equal to the current soil
// otherwise this is might not be a valid corner
(int, int) testPos1 = (row + (dRow * -1), col);
(int, int) testPos2 = (row, col + (dCol * -1));
if ((!_input.TryGetValue(testPos1, out char? pos1) || pos1 != soil) &&
(!_input.TryGetValue(testPos2, out char? pos2) || pos2 != soil))
{
perimeter++;
}
// this position must not be equal to the current soil
// otherwise now this is definitely not a corner
(int, int) testPos3 = (row + dRow, col + dCol);
if (_input.TryGetValue(testPos3, out char? pos3) && pos3 != soil)
{
perimeter += 1;
}
}
}
return perimeter;
}
private int SolvePuzzle(bool example, int stage)
{
_input = ReadInput();
List<ValueTuple<char, int, int>> farmfields = [];
HashSet<ValueTuple<int, int>> visitedAreas = [];
foreach ((string line, int row) in _input.Enumerate())
{
foreach ((char soil, int col) in line.Enumerate())
{
if (!visitedAreas.Add((row, col)))
{
continue;
}
FindArea(row, col, soil, []);
farmfields.Add((soil, _areaPositions.Count, GetPerimeter(soil, stage)));
visitedAreas.UnionWith(_areaPositions);
_areaPositions.Clear();
}
}
int price = 0;
foreach ((char soil, int area, int perimeter) in farmfields)
{
price += (area*perimeter);
}
return price;
}
public override object PartOne()
{
return SolvePuzzle(Example, 1);
}
public override object PartTwo()
{
return SolvePuzzle(Example, 2);
}
public override void Reset()
{
_areaPositions.Clear();
_input = [];
}
}