Add day 4

This commit is contained in:
Tristan 2021-12-06 23:38:44 +01:00
parent 848495b886
commit c1d5089d0a
Signed by: trizz
GPG Key ID: 0A93DEC67165EB47
5 changed files with 918 additions and 0 deletions

601
data/day4/data.txt Normal file
View File

@ -0,0 +1,601 @@
91,17,64,45,8,13,47,19,52,68,63,76,82,44,28,56,37,2,78,48,32,58,72,53,9,85,77,89,36,22,49,86,51,99,6,92,80,87,7,25,31,66,84,4,98,67,46,61,59,79,0,3,38,27,23,95,20,35,14,30,26,33,42,93,12,57,11,54,50,75,90,41,88,96,40,81,24,94,18,39,70,34,21,55,5,29,71,83,1,60,74,69,10,62,43,73,97,65,15,16
83 40 67 98 4
50 74 31 30 3
75 64 79 61 5
12 59 26 25 72
36 33 18 54 10
68 56 28 57 12
78 66 20 85 51
35 23 7 99 44
86 37 8 45 49
40 77 32 6 88
75 15 20 79 8
81 69 54 33 28
9 53 48 95 27
65 84 40 71 36
13 31 6 68 29
94 6 30 16 74
91 47 66 31 90
14 56 45 55 20
58 70 27 46 73
77 67 97 51 54
60 12 49 80 52
15 27 85 82 48
21 76 83 55 54
8 5 4 38 47
73 2 86 44 99
64 60 6 38 37
3 69 21 24 11
36 88 16 55 41
78 7 81 95 91
27 34 92 39 30
38 57 20 68 49
21 18 69 97 60
34 92 0 59 62
10 43 93 87 64
53 35 94 76 61
48 74 58 13 54
57 18 37 92 78
89 10 25 97 43
38 99 64 6 66
21 83 29 93 95
94 37 98 87 51
50 65 77 83 95
68 4 91 53 32
56 26 15 2 80
20 55 58 81 33
73 32 66 38 89
18 79 40 78 55
26 63 93 60 98
42 65 96 47 57
45 75 72 23 35
64 28 21 80 27
93 58 71 67 11
61 20 74 13 90
76 35 46 94 40
92 2 4 85 69
22 70 87 31 61
74 78 58 4 90
63 28 24 35 84
59 8 89 88 47
17 48 80 33 32
57 7 30 39 19
1 13 41 15 50
44 72 2 5 70
34 93 60 80 69
49 14 25 10 33
45 41 77 89 27
68 99 11 32 95
15 4 72 98 52
53 28 14 75 44
57 9 62 92 69
7 21 2 73 40
52 60 57 53 65
63 86 36 82 44
14 28 39 12 80
66 64 91 50 51
82 5 38 41 95
70 52 11 21 51
81 20 0 14 83
57 36 60 59 42
77 13 85 32 63
91 40 42 3 50
22 24 81 31 93
9 79 82 43 89
6 77 76 26 37
29 8 53 23 4
7 78 32 44 74
29 3 84 38 79
58 41 87 88 30
68 19 72 81 47
15 63 52 6 26
20 41 92 84 25
9 4 96 85 66
49 15 50 89 19
48 45 82 86 60
29 18 53 47 16
75 39 45 31 73
91 86 69 94 66
28 61 17 20 0
88 21 89 41 37
35 2 10 18 82
80 23 4 73 93
89 8 20 12 45
74 99 58 90 67
50 85 35 88 55
18 65 42 47 48
16 38 65 64 25
20 74 37 15 82
23 76 97 48 53
60 93 85 1 35
77 10 59 2 58
11 9 57 40 46
35 88 29 52 17
30 2 7 6 0
13 63 44 68 59
83 98 5 50 65
82 40 2 14 50
7 31 91 19 11
51 42 56 44 6
66 74 22 95 64
63 1 17 86 24
18 19 66 63 80
65 23 74 22 85
5 7 37 75 51
38 58 68 83 32
40 29 31 15 43
37 54 13 77 31
57 96 28 87 95
10 11 19 49 45
12 21 79 56 24
34 64 84 69 17
6 33 48 61 0
85 34 7 84 37
25 46 59 76 82
18 62 20 44 2
12 78 60 56 99
95 6 1 39 2
46 34 28 64 22
48 23 89 56 55
44 81 82 43 74
65 31 94 49 91
69 42 27 52 54
79 60 62 83 38
5 21 56 48 99
51 40 15 7 24
92 10 66 64 88
99 18 22 52 81
21 42 13 71 59
91 38 68 10 25
54 19 76 60 24
41 92 2 3 64
76 5 25 55 84
70 15 89 67 68
34 86 11 4 6
9 23 43 41 52
58 10 88 38 0
83 91 85 81 86
5 10 89 6 48
45 77 2 9 90
74 8 57 75 67
73 30 49 96 15
66 13 82 89 20
5 67 94 64 0
58 73 4 62 49
59 28 75 79 44
54 71 57 33 36
23 36 29 80 30
51 91 77 2 84
78 90 15 21 75
28 93 22 55 16
67 50 58 60 68
82 80 37 91 7
54 81 85 25 24
33 36 89 30 56
83 95 99 48 10
4 44 1 55 79
9 13 53 20 26
7 31 49 84 58
51 91 90 68 55
19 38 23 81 33
34 99 85 37 54
44 66 81 78 15
31 14 48 65 0
26 10 20 4 41
77 68 95 34 73
74 12 36 3 60
6 24 78 58 36
30 51 75 13 40
17 1 3 42 59
64 20 4 18 79
37 61 84 63 7
41 83 1 75 18
14 56 67 32 22
69 80 46 84 49
72 21 9 10 35
4 37 28 40 12
56 80 47 17 70
12 22 77 81 11
61 30 58 60 71
52 0 25 86 65
59 28 79 20 26
70 75 81 18 67
2 85 73 8 17
74 3 34 92 30
51 72 84 56 45
37 90 31 97 78
2 73 71 43 69
6 54 89 57 93
81 0 39 25 90
79 27 92 29 15
45 76 87 11 91
98 35 51 49 34
23 12 77 27 82
6 89 0 76 46
81 48 99 45 90
10 75 17 96 29
45 19 82 93 0
84 24 73 2 98
94 46 7 48 56
80 34 5 18 31
58 33 83 29 55
66 81 99 54 63
21 94 72 77 64
58 52 85 46 68
5 6 78 42 4
76 38 51 24 33
93 26 5 59 67
13 84 76 4 69
0 17 30 83 48
8 53 32 14 92
94 18 66 46 61
28 48 38 6 25
70 39 71 77 22
66 94 18 43 36
30 67 57 9 90
15 34 50 3 86
11 90 99 92 87
78 79 56 21 50
19 18 22 20 30
95 41 59 85 26
66 58 46 38 57
49 92 2 93 77
46 89 44 57 19
53 8 32 18 88
54 95 59 70 10
72 84 86 42 81
44 78 25 4 57
72 7 42 94 8
61 79 11 29 59
22 82 6 90 12
98 77 5 68 50
48 41 64 15 57
76 7 52 53 93
70 84 94 38 35
47 18 13 51 21
77 62 63 3 65
31 33 48 79 69
30 9 83 53 50
60 94 36 2 28
59 19 10 5 40
26 41 72 14 96
0 16 49 75 17
28 20 21 99 94
15 8 4 68 71
23 53 76 19 74
79 61 72 70 52
70 89 12 80 76
14 18 16 4 91
34 64 43 51 71
6 78 30 5 13
57 42 15 73 24
64 99 72 41 54
21 29 25 40 9
92 48 82 70 98
65 62 8 78 27
71 86 36 34 23
23 19 72 77 63
85 0 61 40 14
69 76 18 56 95
68 66 28 79 13
83 84 45 89 2
18 40 28 70 37
80 30 67 96 34
77 25 97 32 11
48 46 89 14 29
2 8 95 0 12
0 26 1 9 30
17 2 78 18 65
84 7 61 93 81
80 44 82 23 99
72 95 19 60 28
37 39 0 20 21
91 36 93 16 22
53 95 26 72 25
97 33 60 55 65
79 56 73 29 75
22 58 99 57 28
2 56 93 91 18
44 64 92 85 46
70 47 89 27 54
83 5 48 97 72
72 1 73 68 36
31 8 14 41 35
23 96 7 92 83
56 39 77 93 91
20 28 67 10 11
62 27 17 54 0
35 60 73 20 5
23 58 46 99 75
19 53 79 70 88
31 85 77 1 32
22 90 81 42 55
70 78 86 19 94
1 43 15 33 51
84 96 87 58 6
49 64 4 59 23
82 63 58 75 89
35 37 52 80 24
93 50 76 79 1
86 59 30 92 7
42 11 55 70 22
83 3 71 28 95
70 23 68 57 1
60 6 19 63 32
64 55 97 81 49
91 80 88 5 35
23 68 51 62 20
70 52 98 34 41
12 21 85 43 84
69 49 36 28 0
76 30 58 91 60
30 72 6 41 43
67 79 46 96 99
58 71 39 87 69
17 18 11 57 25
45 75 16 33 42
22 75 24 74 90
34 70 44 86 23
29 59 68 4 48
88 45 92 27 49
47 77 26 99 82
42 29 21 74 33
64 37 38 50 84
46 44 41 1 67
53 66 96 68 59
6 94 11 31 99
24 32 71 87 57
42 26 55 80 99
82 27 16 19 92
96 48 62 31 61
60 89 95 18 6
99 33 55 71 29
75 37 23 27 98
2 78 90 18 35
59 10 56 0 6
12 19 76 70 96
33 37 23 61 80
6 13 68 51 76
92 25 3 95 55
99 63 17 52 30
11 94 42 5 98
77 37 25 14 73
95 90 10 19 72
78 30 44 47 91
3 60 32 5 66
21 55 87 98 6
6 60 82 90 98
21 70 54 66 27
37 64 55 10 14
57 25 84 50 20
42 59 85 3 73
74 84 92 10 51
57 82 93 90 44
41 43 76 48 59
79 49 69 16 72
37 29 63 15 68
37 90 97 86 18
2 83 30 53 92
45 35 78 47 40
67 61 17 14 84
32 33 81 10 11
46 48 39 3 50
83 29 91 73 67
25 43 89 71 36
63 62 78 95 18
82 34 23 85 11
19 68 80 50 13
1 45 51 27 39
98 26 24 46 49
14 92 63 88 66
15 44 84 47 94
19 39 93 43 86
91 58 3 69 41
18 36 95 52 83
12 6 22 48 0
25 70 40 88 73
95 11 94 13 14
64 87 57 98 49
47 88 84 61 2
46 21 15 74 59
82 73 78 3 51
18 72 29 7 36
96 67 81 78 23
43 40 44 47 98
41 26 15 90 71
42 62 93 70 2
17 8 59 25 33
81 47 55 99 48
86 14 71 54 50
90 11 23 18 0
97 65 82 68 42
50 54 68 90 83
10 28 77 55 61
38 60 52 80 44
40 81 14 24 87
51 82 42 30 8
54 5 64 22 60
70 19 83 11 45
46 39 2 56 6
61 8 28 20 94
0 4 81 34 84
96 21 48 89 15
91 40 9 97 65
26 58 10 18 78
98 79 29 80 28
17 59 43 84 99
67 73 21 9 31
68 37 26 65 84
63 24 42 27 40
61 25 30 34 35
53 23 48 81 29
24 34 5 67 62
89 85 68 37 78
42 87 13 49 41
74 55 70 86 76
73 94 97 63 48
88 24 6 75 30
77 64 16 34 93
36 76 0 40 81
67 14 89 84 95
32 19 18 66 9
97 71 65 30 69
41 21 40 31 33
50 55 35 52 53
4 51 13 81 72
12 83 14 64 18
97 7 8 74 10
3 92 31 25 41
20 32 45 72 55
1 43 49 98 27
99 54 57 13 76
86 81 67 6 97
34 18 96 43 56
59 75 17 26 9
0 38 60 94 14
4 55 64 61 88
37 15 48 43 66
45 54 90 81 47
63 64 28 82 93
34 52 6 99 61
49 12 71 23 46
90 87 89 97 1
48 0 82 60 43
55 30 68 25 83
78 3 23 16 66
98 2 19 63 17
89 52 49 14 38
69 12 50 17 90
58 53 26 20 29
39 65 43 7 5
84 68 94 85 25
95 25 42 36 47
50 54 83 84 37
94 70 99 79 18
57 8 69 52 31
66 20 35 71 38
81 18 47 68 15
3 50 16 83 37
34 31 9 57 76
74 95 40 63 48
13 28 20 43 66
52 21 62 41 67
22 56 36 18 23
59 44 27 73 3
72 50 19 33 76
45 55 70 46 92
72 96 50 83 68
31 78 59 57 93
43 58 17 52 35
87 34 91 76 0
54 75 53 25 62
21 53 68 5 80
47 67 6 81 9
64 46 35 26 39
50 24 84 45 71
66 15 83 3 97
22 97 31 90 63
21 51 38 74 78
10 64 92 82 1
70 12 75 16 14
68 50 35 73 26

19
data/day4/example.txt Normal file
View File

@ -0,0 +1,19 @@
7,4,9,5,11,17,23,2,0,14,21,24,10,16,13,6,15,25,12,22,18,20,8,19,3,26,1
22 13 17 11 0
8 2 23 4 24
21 9 14 16 7
6 10 3 18 5
1 12 20 15 19
3 15 0 2 22
9 18 13 17 5
19 8 7 25 23
20 11 10 24 4
14 21 16 12 6
14 21 17 24 4
10 16 15 9 19
18 8 23 26 20
22 11 13 6 5
2 0 12 3 7

79
data/day4/puzzle.md Normal file
View File

@ -0,0 +1,79 @@
# Day 4: Giant Squid
[https://adventofcode.com/2021/day/4](https://adventofcode.com/2021/day/4)
## Description
### Part One
You're already almost 1.5km (almost a mile) below the surface of the ocean, already so deep that you can't see any sunlight. What you _can_ see, however, is a giant squid that has attached itself to the outside of your submarine.
Maybe it wants to play [bingo](https://en.wikipedia.org/wiki/Bingo_(American_version))?
Bingo is played on a set of boards each consisting of a 5x5 grid of numbers. Numbers are chosen at random, and the chosen number is _marked_ on all boards on which it appears. (Numbers may not appear on all boards.) If all numbers in any row or any column of a board are marked, that board _wins_. (Diagonals don't count.)
The submarine has a _bingo subsystem_ to help passengers (currently, you and the giant squid) pass the time. It automatically generates a random order in which to draw numbers and a random set of boards (your puzzle input). For example:
7,4,9,5,11,17,23,2,0,14,21,24,10,16,13,6,15,25,12,22,18,20,8,19,3,26,1
22 13 17 11 0
8 2 23 4 24
21 9 14 16 7
6 10 3 18 5
1 12 20 15 19
3 15 0 2 22
9 18 13 17 5
19 8 7 25 23
20 11 10 24 4
14 21 16 12 6
14 21 17 24 4
10 16 15 9 19
18 8 23 26 20
22 11 13 6 5
2 0 12 3 7
After the first five numbers are drawn (`7`, `4`, `9`, `5`, and `11`), there are no winners, but the boards are marked as follows (shown here adjacent to each other to save space):
22 13 17 11 0 3 15 0 2 22 14 21 17 24 4
8 2 23 4 24 9 18 13 17 5 10 16 15 9 19
21 9 14 16 7 19 8 7 25 23 18 8 23 26 20
6 10 3 18 5 20 11 10 24 4 22 11 13 6 5
1 12 20 15 19 14 21 16 12 6 2 0 12 3 7
After the next six numbers are drawn (`17`, `23`, `2`, `0`, `14`, and `21`), there are still no winners:
22 13 17 11 0 3 15 0 2 22 14 21 17 24 4
8 2 23 4 24 9 18 13 17 5 10 16 15 9 19
21 9 14 16 7 19 8 7 25 23 18 8 23 26 20
6 10 3 18 5 20 11 10 24 4 22 11 13 6 5
1 12 20 15 19 14 21 16 12 6 2 0 12 3 7
Finally, `24` is drawn:
22 13 17 11 0 3 15 0 2 22 14 21 17 24 4
8 2 23 4 24 9 18 13 17 5 10 16 15 9 19
21 9 14 16 7 19 8 7 25 23 18 8 23 26 20
6 10 3 18 5 20 11 10 24 4 22 11 13 6 5
1 12 20 15 19 14 21 16 12 6 2 0 12 3 7
At this point, the third board _wins_ because it has at least one complete row or column of marked numbers (in this case, the entire top row is marked: _`14 21 17 24 4`_).
The _score_ of the winning board can now be calculated. Start by finding the _sum of all unmarked numbers_ on that board; in this case, the sum is `188`. Then, multiply that sum by _the number that was just called_ when the board won, `24`, to get the final score, `188 * 24 = 4512`.
To guarantee victory against the giant squid, figure out which board will win first. _What will your final score be if you choose that board?_
### Part Two
On the other hand, it might be wise to try a different strategy: <span title="That's 'cuz a submarine don't pull things' antennas out of their sockets when they lose. Giant squid are known to do that.">let the giant squid win</span>.
You aren't sure how many bingo boards a giant squid could play at once, so rather than waste time counting its arms, the safe thing to do is to _figure out which board will win last_ and choose that one. That way, no matter which boards it picks, it will win for sure.
In the above example, the second board is the last to win, which happens after `13` is eventually called and its middle column is completely marked. If you were to keep playing until this point, the second board would have a sum of unmarked numbers equal to `148` for a final score of `148 * 13 = 1924`.
Figure out which board will win last. _Once it wins, what would its final score be?_

195
src/Day4.php Normal file
View File

@ -0,0 +1,195 @@
<?php
namespace AdventOfCode21;
class Day4 extends AbstractCommand
{
/**
* {@inheritdoc}
*/
protected static int $day = 4;
/**
* @param int[] $winningCard
* @param int $number
* @psalm-param array<int, array<array-key, bool|int>|string> $winningCard
*
* @return int
*/
protected function calculateScore(array $winningCard, int $number): int
{
$return = [];
array_walk_recursive($winningCard, static function (bool $value, int $key) use (&$return) {
$return[$key] = $value;
});
$unusedNumbers = array_keys(array_filter($return, static fn (bool $value) => !$value));
return (int) array_sum($unusedNumbers) * $number;
}
/**
* {@inheritdoc}
*/
protected function part1(array $data): int|string
{
return $this->playBingo($data, firstWins: true);
}
/**
* {@inheritdoc}
*/
protected function part2(array $data): int|string
{
return $this->playBingo($data, firstWins: false);
}
/**
* @param string $numberList
* @param string $separator
*
* @return int[]
*
* @psalm-return array<int, int>
*/
protected function explodeNumbers(string $numberList, string $separator): array
{
return array_map(
static fn ($value) => (int) $value,
array_filter(
explode($separator, $numberList),
static fn (string $value) => $value !== ''
)
);
}
/**
* @param string[] $data
* @param bool $firstWins
*
* @return int|string
*/
protected function playBingo(array $data, bool $firstWins = true): int|string
{
$numbers = $this->explodeNumbers(array_shift($data), ',');
$cards = $this->setupCards($data);
$finishedCards = [];
// Call the numbers.
foreach ($numbers as $number) {
/**
* @var int $cardIndex
* @var int[] $cardRows
*/
foreach ($cards as $cardIndex => $cardRows) {
if (isset($finishedCards[$cardIndex])) {
continue;
}
/**
* @var int $cardRowIndex
* @var int[] $cardRow
*/
foreach ($cardRows as $cardRowIndex => $cardRow) {
if (isset($cardRow[$number])) {
$cards[$cardIndex][$cardRowIndex][$number] = true;
}
}
}
$winningCards = $this->checkCards($cards, $finishedCards);
if (empty($winningCards)) {
continue;
}
foreach ($winningCards as $winningCard) {
$lastWinningCard = $cards[$winningCard];
$lastWinNumber = $number;
$finishedCards[$winningCard] = true;
if ($firstWins) {
return $this->calculateScore($lastWinningCard, $number);
}
}
}
if (isset($lastWinningCard, $lastWinNumber)) {
return $this->calculateScore($lastWinningCard, $lastWinNumber);
}
return 'Computer says no...';
}
/**
* @param string[] $data
*
* @return ((false|int)[]|string)[][]
*
* @psalm-return array<int, array<int, array<false|int>|string>>
*/
protected function setupCards(array $data): array
{
$cards = array_chunk($data, 5);
foreach ($cards as $card => $rows) {
$cards[$card] = array_map(fn ($value) => $this->explodeNumbers($value, ' '), $rows);
foreach ($cards[$card] as $row => $number) {
$cards[$card][$row] = array_fill_keys(array_values($number), false);
}
}
return $cards;
}
/**
* @return int[]
*
* @psalm-return list<int>
*/
protected function checkCards(array $cards, array $finishedCards): array
{
$winningCards = [];
// Check rows
/**
* @var int $cardIndex
* @var int[] $rows
*/
foreach ($cards as $cardIndex => $rows) {
if (isset($finishedCards[$cardIndex])) {
continue;
}
/** @var int[] $row */
foreach ($rows as $row) {
if ($this->arrayHasSingleValue($row, true)) {
$winningCards[] = $cardIndex;
}
}
// Get the vertical numbers.
$colValues = [];
for ($rowIndex = 0; $rowIndex < 5; ++$rowIndex) {
for ($colIndex = 0; $colIndex < 5; ++$colIndex) {
if (!isset($colValues[$colIndex])) {
$colValues[$colIndex] = [];
}
$colValues[$colIndex] += array_slice((array) $rows[$rowIndex], $colIndex, 1, preserve_keys: true);
}
}
// See if there's a bingo on the vertical numbers.
foreach ($colValues as $colIndex => $colValue) {
if ($this->arrayHasSingleValue($colValue, true)) {
$winningCards[] = $cardIndex;
}
}
}
return $winningCards;
}
protected function arrayHasSingleValue(array $array, bool $value): bool
{
return count(array_unique($array)) === 1 && end($array) === $value;
}
}

24
tests/Day4Test.php Normal file
View File

@ -0,0 +1,24 @@
<?php
namespace Tests;
use AdventOfCode21\Day4;
/**
* @internal
*/
class Day4Test extends AbstractTestCase
{
public static int $part1ExampleResult = 4512;
public static int $part1Result = 60368;
public static int $part2ExampleResult = 1924;
public static int $part2Result = 17435;
public function setupDay(): Day4
{
return new class() extends Day4 {
use ReturnTestableResults;
};
}
}