-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDay9.php
144 lines (124 loc) · 4.09 KB
/
Day9.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
<?php
declare(strict_types=1);
namespace App\Days;
use App\Contracts\Day;
use Illuminate\Support\Collection;
class Day9 extends Day
{
public const EXAMPLE1 = <<<eof
R 4
U 4
L 3
D 1
R 4
D 1
L 5
R 2
eof;
public const EXAMPLE2 = <<<eof
R 5
U 8
L 8
D 3
R 17
D 10
L 25
U 20
eof;
// directions we can travel
private array $directions = [
'U' => [-1, 0],
'R' => [0, 1],
'D' => [1, 0],
'L' => [0, -1],
];
/**
* How many positions does the tail of the rope visit at least once?
*/
public function solvePart1(mixed $input): int|string|null
{
$input = $this
->parseInput($input)
->toArray();
$visited = [];
$head = [0, 0];
$tail = [0, 0];
foreach ($input as [$direction, $amount]) {
$pos = $this->directions[$direction];
while ($amount-- > 0) {
$head[0] += $pos[0];
$head[1] += $pos[1];
// now we gotta calculate where to move the tail based on the following rules:
// If the head is ever two steps directly up, down, left, or right from the tail, the tail must also move one step in that direction so it remains close enough
// Otherwise, if the head and tail aren't touching and aren't in the same row or column, the tail always moves one step diagonally to keep up
$tail = $this->moveTail($head, $tail);
$key = sprintf('%s-%s', $tail[0], $tail[1]);
$visited[$key] = 'T';
}
}
return count($visited);
}
/**
* How many positions does the tail of the rope visit at least once?
*/
public function solvePart2(mixed $input): int|string|null
{
$input = $this
->parseInput($input)
->toArray();
$visited = [];
$knots = array_fill(0, 10, [0, 0]);
foreach ($input as [$direction, $amount]) {
$pos = $this->directions[$direction];
while ($amount-- > 0) {
// move the head once in the direction
$knots[0][0] += $pos[0];
$knots[0][1] += $pos[1];
foreach ($knots as $id => &$tail) {
// skip the head
if (0 === $id) {
continue;
}
$head = &$knots[$id - 1];
$tail = $this->moveTail($head, $tail);
}
unset($tail);
$key = sprintf('%s-%s', $knots[9][0], $knots[9][1]);
$visited[$key] = $id;
}
}
return count($visited);
}
protected function moveTail(array $head, array $tail): array
{
[$headX, $headY] = $head;
[$tailX, $tailY] = $tail;
if (2 === abs($headX - $tailX) + abs($headY - $tailY)) {
// Head is two steps directly up, down, left, or right from the tail
// Move the tail one step in the same direction as the head
match (true) {
2 === $headX - $tailX => ++$tail[0],
-2 === $headX - $tailX => --$tail[0],
2 === $headY - $tailY => ++$tail[1],
-2 === $headY - $tailY => --$tail[1],
default => null
};
} elseif ($headX !== $tailX && $headY !== $tailY) {
// Head and tail aren't touching and aren't in the same row or column
// Move the tail one step diagonally to keep up
$tail[0] += $headX <=> $tailX;
$tail[1] += $headY <=> $tailY;
}
return $tail;
}
protected function parseInput(mixed $input): Collection
{
$input = is_array($input) ? $input : explode("\n", $input);
return collect($input)
->map(fn ($line) => explode(' ', $line));
}
public function getExample2(): mixed
{
return static::EXAMPLE2;
}
}