-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDay04.php
207 lines (184 loc) · 6.04 KB
/
Day04.php
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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
<?php
/**
* Solution for Advent of Code Day 04.
*
* @Author: Digitalbüro Mokorana
* @Date: 2024-12-04 08:31:04
* @Last Modified by: Stefan Koch <stefan.koch@mokorana.de>
* @Last Modified time: 2024-12-04 16:38:53
*
* @package Aoc
*/
namespace Aoc;
/**
* The class is used, it's just called dynamically from App.php.
*
* @psalm-suppress UnusedClass
*/
class Day04 extends AbstractDay
{
/**
* Split input lines into arrays of characters.
*
* @param array<string> $lines The input lines.
* @return array<array<string>> The split rows.
*/
private function splitRows(array $lines): array
{
return array_map('str_split', $lines);
}
/**
* Transform rows to columns for vertical matching.
*
* @param array<array<string>> $rows The grid rows.
* @return array<array<string>> The transformed columns.
*/
private function transformToColumns(array $rows): array
{
$columns = [];
foreach (array_keys($rows[0]) as $colIndex) {
$columns[] = array_column($rows, $colIndex);
}
return $columns;
}
/**
* Collect diagonals from the grid.
*
* @param array<array<string>> $rows The grid rows.
* @param bool $topLeftToBottomRight Whether to collect TL-BR diagonals.
* @return array<string> The collected diagonals.
*/
private function collectDiagonals(array $rows, bool $topLeftToBottomRight): array
{
$diagonals = [];
foreach ($rows as $rowIndex => $row) {
foreach (array_keys($row) as $colIndex) {
// TL-BR diagonals start from top row or leftmost column
// TR-BL diagonals start from top row or rightmost column
$isStartingPoint = $topLeftToBottomRight
? ($rowIndex === 0 || $colIndex === 0)
: ($rowIndex === 0 || $colIndex === count($row) - 1);
if ($isStartingPoint) {
$diagonal = '';
$i = $rowIndex;
$j = $colIndex;
while (isset($rows[$i][$j])) {
$diagonal .= $rows[$i][$j];
// Adjust traversal direction based on diagonal type
$i++;
$topLeftToBottomRight ? ++$j : --$j;
}
$diagonals[] = $diagonal;
}
}
}
return $diagonals;
}
/**
* Count matches for "XMAS" and "SAMX" in an array of strings.
*
* @param array<string>|array<array<string>> $grid The grid or flat list to search.
* @return int The number of matches found.
*/
private function countMatches(array $grid): int
{
$sum = 0;
foreach ($grid as $line) {
$joined = is_array($line) ? implode('', $line) : $line;
$sum += preg_match_all('/XMAS/i', $joined);
$sum += preg_match_all('/SAMX/i', $joined);
}
return $sum;
}
/**
* Find all valid "A" anchors with enough space around them.
*
* @param array<array<string>> $rows The grid rows.
* @return array<array<int>> The coordinates of valid anchors.
*/
private function findValidAnchors(array $rows): array
{
$anchors = [];
for ($row = 1, $numRows = count($rows) - 1; $row < $numRows; $row++) {
for ($col = 1, $numCols = count($rows[$row]) - 1; $col < $numCols; $col++) {
if ($rows[$row][$col] === 'A') {
$anchors[] = [$row, $col];
}
}
}
return $anchors;
}
/**
* Count valid patterns around anchors.
*
* @param array<array<string>> $rows The grid rows.
* @param array<array<int>> $anchors The valid "A" anchors.
* @return int The number of valid patterns.
*/
private function countValidPatterns(array $rows, array $anchors): int
{
$sum = 0;
foreach ($anchors as [$row, $col]) {
$corners = [
[-1, -1], [-1, 1],
[1, -1], [1, 1]
];
$mCount = 0;
$sCount = 0;
foreach ($corners as [$rOffset, $cOffset]) {
$char = $rows[$row + $rOffset][$col + $cOffset];
$char === 'M' ? $mCount++ : ($char === 'S' ? $sCount++ : null);
}
if (
$mCount === 2 && $sCount === 2 &&
$rows[$row - 1][$col - 1] !== $rows[$row + 1][$col + 1]
) {
$sum++;
}
}
return $sum;
}
/**
* Solve part 1 of the challenge: Count matches for XMAS and SAMX.
*
* @param array<string> $lines The grid lines.
* @return int The total number of matches.
*/
public function solvePart1(array $lines): int
{
$rows = $this->splitRows($lines);
$columns = $this->transformToColumns($rows);
$diagonals = array_merge(
$this->collectDiagonals($rows, true), // Top-left to Bottom-right
$this->collectDiagonals($rows, false) // Top-right to Bottom-left
);
return $this->countMatches($rows) +
$this->countMatches($columns) +
$this->countMatches($diagonals);
}
/**
* Solve part 2 of the challenge: Count matches of X-MAS patterns
*
* @param array<string> $lines The grid lines.
* @return int The total number of valid "A" patterns.
*/
public function solvePart2(array $lines): int
{
$rows = $this->splitRows($lines);
$validAnchors = $this->findValidAnchors($rows);
return $this->countValidPatterns($rows, $validAnchors);
}
/**
* Main solution function to handle input and return results for both parts.
*
* @return array<string, int>
*/
public function solve(): array
{
$lines = explode("\n", $this->getInputString());
return [
"Part 1" => $this->solvePart1($lines),
"Part 2" => $this->solvePart2($lines)
];
}
}