Move solutions out of the source itself
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
63
data/Y21/day1/Day1.php
Normal file
63
data/Y21/day1/Day1.php
Normal file
@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace trizz\AdventOfCode\Y21;
|
||||
|
||||
use trizz\AdventOfCode\Solution;
|
||||
|
||||
final class Day1 extends Solution
|
||||
{
|
||||
public static null|int|string $part1ExampleResult = 7;
|
||||
|
||||
public static null|int|string $part1Result = 1688;
|
||||
|
||||
public static null|int|string $part2ExampleResult = 5;
|
||||
|
||||
public static null|int|string $part2Result = 1728;
|
||||
|
||||
#[\Override]
|
||||
public function part1(array $data): int
|
||||
{
|
||||
$previous = null;
|
||||
$increases = 0;
|
||||
|
||||
/** @var int $current */
|
||||
foreach ($data as $current) {
|
||||
if ($previous !== null && $previous < $current) {
|
||||
++$increases;
|
||||
}
|
||||
|
||||
$previous = $current;
|
||||
}
|
||||
|
||||
return $increases;
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function part2(array $data): int
|
||||
{
|
||||
$previousSum = null;
|
||||
$increases = 0;
|
||||
|
||||
/**
|
||||
* @var int $index
|
||||
* @var int $indexValue
|
||||
*/
|
||||
foreach ($data as $index => $indexValue) {
|
||||
// If no 'next' indexes are available, skip.
|
||||
if (!isset($data[$index + 1], $data[$index + 2])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Calculate the sum of the current value and the next two.
|
||||
$newSum = $indexValue + (int) $data[$index + 1] + (int) $data[$index + 2];
|
||||
|
||||
if ($previousSum !== null && $previousSum < $newSum) {
|
||||
++$increases;
|
||||
}
|
||||
|
||||
$previousSum = $newSum;
|
||||
}
|
||||
|
||||
return $increases;
|
||||
}
|
||||
}
|
77
data/Y21/day2/Day2.php
Normal file
77
data/Y21/day2/Day2.php
Normal file
@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace trizz\AdventOfCode\Y21;
|
||||
|
||||
use trizz\AdventOfCode\Solution;
|
||||
|
||||
final class Day2 extends Solution
|
||||
{
|
||||
public static null|int|string $part1ExampleResult = 150;
|
||||
|
||||
public static null|int|string $part1Result = 1_654_760;
|
||||
|
||||
public static null|int|string $part2ExampleResult = 900;
|
||||
|
||||
public static null|int|string $part2Result = 1_956_047_400;
|
||||
|
||||
#[\Override]
|
||||
public function part1(array $data): int
|
||||
{
|
||||
$depth = 0;
|
||||
$horizontal = 0;
|
||||
|
||||
foreach ($data as $current) {
|
||||
/**
|
||||
* @var string $direction
|
||||
* @var int $distance
|
||||
*/
|
||||
[$direction, $distance] = explode(' ', $current);
|
||||
|
||||
match ($direction) {
|
||||
'forward' => $horizontal += $distance,
|
||||
'down' => $depth += $distance,
|
||||
'up' => $depth -= $distance,
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
|
||||
return $depth * $horizontal;
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function part2(array $data): int
|
||||
{
|
||||
$aim = 0;
|
||||
$depth = 0;
|
||||
$horizontal = 0;
|
||||
|
||||
foreach ($data as $current) {
|
||||
/**
|
||||
* @var string $direction
|
||||
* @var int $distance
|
||||
*/
|
||||
[$direction, $distance] = explode(' ', $current);
|
||||
|
||||
// Can't use 'match' here because of the multiple expressions for 'forward'.
|
||||
switch ($direction) {
|
||||
case 'forward':
|
||||
$horizontal += $distance;
|
||||
$depth += $distance * $aim;
|
||||
|
||||
break;
|
||||
|
||||
case 'down':
|
||||
$aim += $distance;
|
||||
|
||||
break;
|
||||
|
||||
case 'up':
|
||||
$aim -= $distance;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $horizontal * $depth;
|
||||
}
|
||||
}
|
39
data/Y21/day3/Day3.php
Normal file
39
data/Y21/day3/Day3.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace trizz\AdventOfCode\Y21;
|
||||
|
||||
use trizz\AdventOfCode\Solution;
|
||||
|
||||
final class Day3 extends Solution
|
||||
{
|
||||
public static null|int|string $part1ExampleResult = 198;
|
||||
|
||||
public static null|int|string $part1Result = 3_309_596;
|
||||
|
||||
#[\Override]
|
||||
public function part1(array $data): int
|
||||
{
|
||||
$bits = [];
|
||||
|
||||
foreach ($data as $binary) {
|
||||
$split = str_split($binary);
|
||||
|
||||
foreach ($split as $position => $value) {
|
||||
$bits[$position][] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$gammaRate = '';
|
||||
$epsilonRate = '';
|
||||
|
||||
foreach ($bits as $bit) {
|
||||
$zeros = array_filter($bit, static fn ($value): bool => $value === '0');
|
||||
$ones = array_filter($bit, static fn ($value): bool => $value === '1');
|
||||
|
||||
$gammaRate .= ($ones > $zeros) ? '1' : '0';
|
||||
$epsilonRate .= ($ones < $zeros) ? '1' : '0';
|
||||
}
|
||||
|
||||
return (int) (bindec($gammaRate) * bindec($epsilonRate));
|
||||
}
|
||||
}
|
191
data/Y21/day4/Day4.php
Normal file
191
data/Y21/day4/Day4.php
Normal file
@ -0,0 +1,191 @@
|
||||
<?php
|
||||
|
||||
namespace trizz\AdventOfCode\Y21;
|
||||
|
||||
use trizz\AdventOfCode\Solution;
|
||||
|
||||
final class Day4 extends Solution
|
||||
{
|
||||
public static null|int|string $part1ExampleResult = 4512;
|
||||
|
||||
public static null|int|string $part1Result = 60368;
|
||||
|
||||
public static null|int|string $part2ExampleResult = 1924;
|
||||
|
||||
public static null|int|string $part2Result = 17435;
|
||||
|
||||
#[\Override]
|
||||
public function part1(array $data): int|string
|
||||
{
|
||||
return $this->playBingo($data, firstWins: true);
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function part2(array $data): int|string
|
||||
{
|
||||
return $this->playBingo($data, firstWins: false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $winningCard
|
||||
*
|
||||
* @psalm-param array<int, array<array-key, bool|int>|string> $winningCard
|
||||
*/
|
||||
private function calculateScore(array $winningCard, int $number): int
|
||||
{
|
||||
$return = [];
|
||||
array_walk_recursive($winningCard, static function (bool $value, int $key) use (&$return): void {
|
||||
$return[$key] = $value;
|
||||
});
|
||||
$unusedNumbers = array_keys(array_filter($return, static fn (bool $value): bool => !$value));
|
||||
|
||||
return (int) array_sum($unusedNumbers) * $number;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param non-empty-string $separator
|
||||
*
|
||||
* @return int[]
|
||||
*
|
||||
* @psalm-return array<int, int>
|
||||
*/
|
||||
private function explodeNumbers(string $numberList, string $separator): array
|
||||
{
|
||||
return array_map(
|
||||
static fn ($value): int => (int) $value,
|
||||
array_filter(
|
||||
explode($separator, $numberList),
|
||||
static fn (string $value): bool => $value !== ''
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $data
|
||||
*/
|
||||
private 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 ($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>>
|
||||
*/
|
||||
private function setupCards(array $data): array
|
||||
{
|
||||
$cards = array_chunk($data, 5);
|
||||
foreach ($cards as $card => $rows) {
|
||||
$cards[$card] = array_map(fn ($value): array => $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>
|
||||
*/
|
||||
private 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;
|
||||
}
|
||||
|
||||
private function arrayHasSingleValue(array $array, bool $value): bool
|
||||
{
|
||||
return count(array_unique($array)) === 1 && end($array) === $value;
|
||||
}
|
||||
}
|
89
data/Y21/day6/Day6.php
Normal file
89
data/Y21/day6/Day6.php
Normal file
@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
namespace trizz\AdventOfCode\Y21;
|
||||
|
||||
use JetBrains\PhpStorm\Immutable;
|
||||
use trizz\AdventOfCode\Solution;
|
||||
|
||||
final class Day6 extends Solution
|
||||
{
|
||||
public static null|int|string $part1ExampleResult = 5934;
|
||||
|
||||
public static null|int|string $part1Result = 350917;
|
||||
|
||||
public static null|int|string $part2ExampleResult = 26_984_457_539;
|
||||
|
||||
public static null|int|string $part2Result = 1_592_918_715_629;
|
||||
|
||||
/**
|
||||
* @var int[]
|
||||
*
|
||||
* @psalm-param array{int: int}
|
||||
*/
|
||||
#[Immutable] private array $startState = [
|
||||
8 => 0,
|
||||
7 => 0,
|
||||
6 => 0,
|
||||
5 => 0,
|
||||
4 => 0,
|
||||
3 => 0,
|
||||
2 => 0,
|
||||
1 => 0,
|
||||
0 => 0,
|
||||
];
|
||||
|
||||
#[\Override]
|
||||
public function part1(array $data): int
|
||||
{
|
||||
return $this->processPuzzle(80, $data[0]);
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function part2(array $data): int
|
||||
{
|
||||
return $this->processPuzzle(256, $data[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $state
|
||||
*
|
||||
* @psalm-param array{int: int} $state
|
||||
*/
|
||||
private function processDay(array $state): array
|
||||
{
|
||||
$newState = $state;
|
||||
|
||||
/**
|
||||
* @var int $key
|
||||
* @var int $stateValue
|
||||
*/
|
||||
foreach ($state as $key => $stateValue) {
|
||||
$newKey = $key - 1;
|
||||
|
||||
if ($newKey < 0) {
|
||||
$newState[8] = $stateValue;
|
||||
$newKey = 6;
|
||||
}
|
||||
|
||||
$newState[$key] -= $stateValue;
|
||||
$newState[$newKey] += $stateValue;
|
||||
}
|
||||
|
||||
return $newState;
|
||||
}
|
||||
|
||||
private function processPuzzle(int $numberOfDays, string $data): int
|
||||
{
|
||||
$state = $this->startState;
|
||||
|
||||
array_map(static function (string $stateValue) use (&$state): void {
|
||||
++$state[(int) $stateValue];
|
||||
}, explode(',', $data));
|
||||
|
||||
for ($day = 0; $day < $numberOfDays; ++$day) {
|
||||
$state = $this->processDay($state);
|
||||
}
|
||||
|
||||
return (int) array_sum($state);
|
||||
}
|
||||
}
|
57
data/Y21/day7/Day7.php
Normal file
57
data/Y21/day7/Day7.php
Normal file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace trizz\AdventOfCode\Y21;
|
||||
|
||||
use trizz\AdventOfCode\Solution;
|
||||
|
||||
final class Day7 extends Solution
|
||||
{
|
||||
public static null|int|string $part1ExampleResult = 37;
|
||||
|
||||
public static null|int|string $part1Result = 344297;
|
||||
|
||||
public static null|int|string $part2ExampleResult = 168;
|
||||
|
||||
public static null|int|string $part2Result = 97_164_301;
|
||||
|
||||
#[\Override]
|
||||
public function part1(array $data): int
|
||||
{
|
||||
return $this->calculateFuel($data[0], forPart2: false);
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function part2(array $data): int
|
||||
{
|
||||
return $this->calculateFuel($data[0], forPart2: true);
|
||||
}
|
||||
|
||||
private function calculateFuel(string $data, bool $forPart2 = false): int
|
||||
{
|
||||
$crabs = array_map(static fn (string $crab): int => (int) $crab, explode(',', $data));
|
||||
|
||||
/** @var array<int, int> $fuelPerPosition */
|
||||
$fuelPerPosition = [];
|
||||
|
||||
$minCrab = min($crabs);
|
||||
$maxCrab = max($crabs);
|
||||
|
||||
for ($position = $minCrab; $position <= $maxCrab; ++$position) {
|
||||
foreach ($crabs as $crab) {
|
||||
if (!isset($fuelPerPosition[$position])) {
|
||||
$fuelPerPosition[$position] = 0;
|
||||
}
|
||||
|
||||
$consumption = abs($position - $crab);
|
||||
|
||||
if ($forPart2) {
|
||||
$consumption = $consumption * ($consumption + 1) / 2;
|
||||
}
|
||||
|
||||
$fuelPerPosition[$position] += $consumption;
|
||||
}
|
||||
}
|
||||
|
||||
return (int) min($fuelPerPosition);
|
||||
}
|
||||
}
|
129
data/Y21/day8/Day8.php
Normal file
129
data/Y21/day8/Day8.php
Normal file
@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
namespace trizz\AdventOfCode\Y21;
|
||||
|
||||
use trizz\AdventOfCode\Solution;
|
||||
use trizz\AdventOfCode\Utils\Arr;
|
||||
use trizz\AdventOfCode\Utils\Str;
|
||||
|
||||
final class Day8 extends Solution
|
||||
{
|
||||
public static null|int|string $part1ExampleResult = 26;
|
||||
|
||||
public static null|int|string $part1Result = 397;
|
||||
|
||||
public static null|int|string $part2ExampleResult = 61229;
|
||||
|
||||
public static null|int|string $part2Result = 1_027_422;
|
||||
|
||||
private array $digitPatterns;
|
||||
|
||||
private array $patternDigits;
|
||||
|
||||
#[\Override]
|
||||
public function part1(array $data): int
|
||||
{
|
||||
$values = array_map(
|
||||
static fn ($item): int => strlen((string) $item),
|
||||
Arr::flatten(
|
||||
array_map(
|
||||
static fn ($item): array => explode(' ', (string) $item),
|
||||
array_map(
|
||||
static fn ($item): string => explode(' | ', (string) $item)[1],
|
||||
$data
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
return count(array_intersect($values, [2, 4, 3, 7]));
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function part2(array $data): int|string
|
||||
{
|
||||
$sequences = [];
|
||||
|
||||
foreach ($data as $line) {
|
||||
$item = explode(' | ', $line);
|
||||
$sequences[] = [
|
||||
'patterns' => array_map(Str::sort(...), explode(' ', $item[0])),
|
||||
'shown' => array_map(Str::sort(...), explode(' ', $item[1])),
|
||||
];
|
||||
}
|
||||
|
||||
$results = [];
|
||||
foreach ($sequences as $sequence) {
|
||||
$this->mapDigits($sequence['patterns']);
|
||||
$output = '';
|
||||
|
||||
foreach ($sequence['shown'] as $pattern) {
|
||||
$output .= $this->patternDigits[$pattern];
|
||||
}
|
||||
|
||||
$results[] = (int) $output;
|
||||
}
|
||||
|
||||
return array_sum($results);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $patterns
|
||||
*
|
||||
* Code based on (sorry, didn't have a clue to solve this one, so I needed some inspiration):
|
||||
*
|
||||
* @see https://github.com/MueR/adventofcode/blob/master/src/AdventOfCode2021/Day08/Day08.php
|
||||
*/
|
||||
public function mapDigits(array $patterns): void
|
||||
{
|
||||
/** @noinspection PackedHashtableOptimizationInspection */
|
||||
$findDigit = [
|
||||
// 1 is the only one with 2 segments
|
||||
1 => static fn (string $pattern): bool => strlen($pattern) === 2,
|
||||
// 7 is the only one with 3 segments
|
||||
7 => static fn (string $pattern): bool => strlen($pattern) === 3,
|
||||
// 4 is the only one with 4 segments
|
||||
4 => static fn (string $pattern): bool => strlen($pattern) === 4,
|
||||
// 8 is the only one with 7 segments
|
||||
8 => static fn (string $pattern): bool => strlen($pattern) === 7,
|
||||
// 9 is 6 segments, matches segments for 4
|
||||
9 => fn (string $pattern): bool => strlen($pattern) === 6 && Str::matchesAll(
|
||||
$pattern,
|
||||
$this->digitPatterns[4] ?? ''
|
||||
),
|
||||
// 0 is 6 segments, matching 1's segments (9 is already out)
|
||||
0 => fn (string $pattern): bool => strlen($pattern) === 6 && Str::matchesAll(
|
||||
$pattern,
|
||||
$this->digitPatterns[1] ?? ''
|
||||
),
|
||||
// 6 is 6 segments, the only one left
|
||||
6 => static fn (string $pattern): bool => strlen($pattern) === 6,
|
||||
// 3 is 5 segments and matches 1's segments
|
||||
3 => fn (string $pattern): bool => strlen($pattern) === 5 && Str::matchesAll(
|
||||
$pattern,
|
||||
$this->digitPatterns[1] ?? ''
|
||||
),
|
||||
// 5 is 5 segments, and 9 has all the segments of 5
|
||||
5 => fn (string $pattern): bool => strlen($pattern) === 5 && Str::matchesAll(
|
||||
$this->digitPatterns[9] ?? '',
|
||||
$pattern
|
||||
),
|
||||
// 2 is the only one remaining
|
||||
2 => static fn (string $pattern): bool => true,
|
||||
];
|
||||
|
||||
foreach ($findDigit as $digit => $test) {
|
||||
foreach ($patterns as $key => $pattern) {
|
||||
if (!$test($pattern)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
unset($patterns[$key]);
|
||||
$this->patternDigits[$pattern] = $digit;
|
||||
$this->digitPatterns[$digit] = $pattern;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
57
data/Y22/day1/Day1.php
Normal file
57
data/Y22/day1/Day1.php
Normal file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace trizzssdddAdventOfCode\Y22;
|
||||
|
||||
use trizz\AdventOfCode\Solution;
|
||||
|
||||
final class Day1 extends Solution
|
||||
{
|
||||
public static null|int|string $part1ExampleResult = 24000;
|
||||
|
||||
public static null|int|string $part1Result = 72240;
|
||||
|
||||
public static null|int|string $part2ExampleResult = 45000;
|
||||
|
||||
public static null|int|string $part2Result = 210957;
|
||||
|
||||
public bool $filterDataOnLoad = false;
|
||||
|
||||
#[\Override]
|
||||
public function part1(array $data): int
|
||||
{
|
||||
return $this->calculateCalories($data)[0];
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function part2(array $data): int
|
||||
{
|
||||
$results = $this->calculateCalories($data);
|
||||
|
||||
return $results[0] + $results[1] + $results[2];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $data
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
private function calculateCalories(array $data): array
|
||||
{
|
||||
$results = [];
|
||||
$tmpResult = 0;
|
||||
foreach ($data as $value) {
|
||||
if ($value !== '') {
|
||||
$tmpResult += (int) $value;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$results[] = $tmpResult;
|
||||
$tmpResult = 0;
|
||||
}
|
||||
|
||||
rsort($results);
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
71
data/Y22/day2/Day2.php
Normal file
71
data/Y22/day2/Day2.php
Normal file
@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace trizz\AdventOfCode\Y22;
|
||||
|
||||
use trizz\AdventOfCode\Solution;
|
||||
use trizz\AdventOfCode\Y22\Day2\RPS;
|
||||
|
||||
final class Day2 extends Solution
|
||||
{
|
||||
public static null|int|string $part1ExampleResult = 15;
|
||||
|
||||
public static null|int|string $part1Result = 10994;
|
||||
|
||||
public static null|int|string $part2ExampleResult = 12;
|
||||
|
||||
public static null|int|string $part2Result = 12526;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
require_once __DIR__.'/RPS.php';
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function part1(array $data): int
|
||||
{
|
||||
$score = 0;
|
||||
foreach ($data as $round) {
|
||||
[$opponentDraw, $ownDraw] = explode(' ', $round);
|
||||
$ownDraw = RPS::fromPuzzleInput($ownDraw);
|
||||
$opponentDraw = RPS::fromPuzzleInput($opponentDraw);
|
||||
$score += $this->calculateScore($ownDraw, $opponentDraw);
|
||||
}
|
||||
|
||||
return $score;
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function part2(array $data): int
|
||||
{
|
||||
$score = 0;
|
||||
foreach ($data as $round) {
|
||||
[$opponentDraw, $expectedResult] = explode(' ', $round);
|
||||
$opponentDraw = RPS::fromPuzzleInput($opponentDraw);
|
||||
|
||||
$score += match ($expectedResult) {
|
||||
'X' => $this->calculateScore($opponentDraw->losingOpposite(), $opponentDraw),
|
||||
'Y' => $this->calculateScore($opponentDraw, $opponentDraw),
|
||||
'Z' => $this->calculateScore($opponentDraw->winningOpposite(), $opponentDraw),
|
||||
default => 0,
|
||||
};
|
||||
}
|
||||
|
||||
return $score;
|
||||
}
|
||||
|
||||
private function calculateScore(RPS $ownDraw, RPS $opponentDraw): int
|
||||
{
|
||||
// Win
|
||||
if ($ownDraw->losingOpposite() === $opponentDraw) {
|
||||
return $ownDraw->score() + 6;
|
||||
}
|
||||
|
||||
// Lost
|
||||
if ($ownDraw->winningOpposite() === $opponentDraw) {
|
||||
return $ownDraw->score();
|
||||
}
|
||||
|
||||
// Draw.
|
||||
return $ownDraw->score() + 3;
|
||||
}
|
||||
}
|
49
data/Y22/day2/RPS.php
Normal file
49
data/Y22/day2/RPS.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace trizz\AdventOfCode\Y22\Day2;
|
||||
|
||||
enum RPS
|
||||
{
|
||||
case ROCK;
|
||||
|
||||
case PAPER;
|
||||
|
||||
case SCISSORS;
|
||||
|
||||
public function score(): int
|
||||
{
|
||||
return match ($this) {
|
||||
self::ROCK => 1,
|
||||
self::PAPER => 2,
|
||||
self::SCISSORS => 3,
|
||||
};
|
||||
}
|
||||
|
||||
public function winningOpposite(): RPS
|
||||
{
|
||||
return match ($this) {
|
||||
self::ROCK => self::PAPER,
|
||||
self::PAPER => self::SCISSORS,
|
||||
self::SCISSORS => self::ROCK,
|
||||
};
|
||||
}
|
||||
|
||||
public function losingOpposite(): RPS
|
||||
{
|
||||
return match ($this) {
|
||||
self::ROCK => self::SCISSORS,
|
||||
self::PAPER => self::ROCK,
|
||||
self::SCISSORS => self::PAPER,
|
||||
};
|
||||
}
|
||||
|
||||
public static function fromPuzzleInput(string $value): RPS
|
||||
{
|
||||
return match ($value) {
|
||||
'A', 'X' => self::ROCK,
|
||||
'B', 'Y' => self::PAPER,
|
||||
'C', 'Z' => self::SCISSORS,
|
||||
default => throw new \LogicException('Invalid value.'),
|
||||
};
|
||||
}
|
||||
}
|
66
data/Y22/day3/Day3.php
Normal file
66
data/Y22/day3/Day3.php
Normal file
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace trizz\AdventOfCode\Y22;
|
||||
|
||||
use trizz\AdventOfCode\Solution;
|
||||
|
||||
final class Day3 extends Solution
|
||||
{
|
||||
public static null|int|string $part1ExampleResult = 157;
|
||||
|
||||
public static null|int|string $part1Result = 8053;
|
||||
|
||||
public static null|int|string $part2ExampleResult = 70;
|
||||
|
||||
public static null|int|string $part2Result = 2425;
|
||||
|
||||
/**
|
||||
* @param array<int, string> $data
|
||||
*/
|
||||
#[\Override]
|
||||
public function part1(array $data): int
|
||||
{
|
||||
$result = 0;
|
||||
foreach ($data as $rucksack) {
|
||||
/** @var int<1, max> $length */
|
||||
$length = max(1, strlen($rucksack) / 2);
|
||||
[$left, $right] = str_split($rucksack, $length);
|
||||
$result += $this->calculateScore([str_split($left), str_split($right)]);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, string> $data
|
||||
*/
|
||||
#[\Override]
|
||||
public function part2(array $data): int
|
||||
{
|
||||
$result = 0;
|
||||
$groups = array_chunk($data, 3);
|
||||
foreach ($groups as $group) {
|
||||
$data = array_map(static fn ($items): array => str_split((string) $items), $group);
|
||||
$result += $this->calculateScore($data);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<array<int, string>> $data
|
||||
*/
|
||||
private function calculateScore(array $data = []): int
|
||||
{
|
||||
$items = array_unique(array_intersect(...$data));
|
||||
|
||||
$result = 0;
|
||||
|
||||
foreach ($items as $item) {
|
||||
$position = ord(strtoupper($item)) - ord('A') + 1;
|
||||
$result += $position + (ctype_upper($item) ? 26 : 0);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
91
data/Y23/day1/Day1.php
Normal file
91
data/Y23/day1/Day1.php
Normal file
@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
namespace trizz\AdventOfCode\Y23;
|
||||
|
||||
use trizz\AdventOfCode\Solution;
|
||||
|
||||
final class Day1 extends Solution
|
||||
{
|
||||
/**
|
||||
* @var array<string,int>
|
||||
*/
|
||||
private const array NUMBER_STRING = [
|
||||
'one' => 1,
|
||||
'two' => 2,
|
||||
'three' => 3,
|
||||
'four' => 4,
|
||||
'five' => 5,
|
||||
'six' => 6,
|
||||
'seven' => 7,
|
||||
'eight' => 8,
|
||||
'nine' => 9,
|
||||
];
|
||||
|
||||
public static null|int|string $part1ExampleResult = 142;
|
||||
|
||||
public static null|int|string $part1Result = 53974;
|
||||
|
||||
public static null|int|string $part2ExampleResult = 281;
|
||||
|
||||
public static null|int|string $part2Result = 52840;
|
||||
|
||||
#[\Override]
|
||||
public function part1(array $data): int
|
||||
{
|
||||
$total = 0;
|
||||
|
||||
foreach ($data as $line) {
|
||||
$total += $this->extractNumbersFromLine($line);
|
||||
}
|
||||
|
||||
return $total;
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function part2(array $data): int
|
||||
{
|
||||
$total = 0;
|
||||
|
||||
foreach ($data as $line) {
|
||||
$total += $this->extractNumbersFromLineWithText($line);
|
||||
}
|
||||
|
||||
return $total;
|
||||
}
|
||||
|
||||
private function extractNumbersFromLine(string $line): int
|
||||
{
|
||||
preg_match_all('#\d#', $line, $numbers);
|
||||
|
||||
if (empty($numbers[0])) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$first = $numbers[0][0];
|
||||
$last = $numbers[0][array_key_last($numbers[0])];
|
||||
|
||||
return (int) ($first.$last);
|
||||
}
|
||||
|
||||
private function extractNumbersFromLineWithText(string $line): int
|
||||
{
|
||||
preg_match_all('#\d|'.implode('|', array_keys(self::NUMBER_STRING)).'#', $line, $numbersStart);
|
||||
|
||||
// Same regex, but match everything in reverse, so the last is the first.
|
||||
$keys = implode('|', array_map(static fn (string $k): string => '('.strrev($k).')', array_keys(self::NUMBER_STRING)));
|
||||
preg_match_all('#\d|'.$keys.'#', strrev($line), $numbersEnd);
|
||||
|
||||
$first = $numbersStart[0][0];
|
||||
$last = strrev((string) $numbersEnd[0][0]);
|
||||
|
||||
if (!ctype_digit((string) $first)) {
|
||||
$first = self::NUMBER_STRING[$first];
|
||||
}
|
||||
|
||||
if (!ctype_digit($last)) {
|
||||
$last = self::NUMBER_STRING[$last];
|
||||
}
|
||||
|
||||
return (int) ($first.$last);
|
||||
}
|
||||
}
|
108
data/Y23/day2/Day2.php
Normal file
108
data/Y23/day2/Day2.php
Normal file
@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
namespace trizz\AdventOfCode\Y23;
|
||||
|
||||
use trizz\AdventOfCode\Solution;
|
||||
|
||||
final class Day2 extends Solution
|
||||
{
|
||||
public static null|int|string $part1ExampleResult = 8;
|
||||
|
||||
public static null|int|string $part1Result = 2810;
|
||||
|
||||
public static null|int|string $part2ExampleResult = 2286;
|
||||
|
||||
public static null|int|string $part2Result = 69110;
|
||||
|
||||
/**
|
||||
* @var array<string, array<string, int>>
|
||||
*/
|
||||
private static array $colorData = [
|
||||
'red' => ['max' => 12],
|
||||
'green' => ['max' => 13],
|
||||
'blue' => ['max' => 14],
|
||||
];
|
||||
|
||||
#[\Override]
|
||||
public function part1(array $data): int
|
||||
{
|
||||
$score = 0;
|
||||
foreach ($data as $line) {
|
||||
[$gameId, $hands] = $this->getHand($line);
|
||||
$validGame = true;
|
||||
foreach ($hands as $hand) {
|
||||
if ($this->isValidHand($hand)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$validGame = false;
|
||||
}
|
||||
|
||||
if ($validGame) {
|
||||
$score += (int) $gameId;
|
||||
}
|
||||
}
|
||||
|
||||
return (int) $score;
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function part2(array $data): int
|
||||
{
|
||||
$score = 0;
|
||||
foreach ($data as $line) {
|
||||
[$gameId, $hands] = $this->getHand($line);
|
||||
$handData = ['red' => 0, 'green' => 0, 'blue' => 0];
|
||||
foreach ($hands as $hand) {
|
||||
$colorsInHand = $this->extractColors($hand);
|
||||
foreach ($handData as $color => $value) {
|
||||
if (($colorsInHand[$color] ?? 0) > $value) {
|
||||
$handData[$color] = $colorsInHand[$color];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$score += array_product($handData);
|
||||
}
|
||||
|
||||
return (int) $score;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<int, string>>
|
||||
*/
|
||||
private function getHand(string $line): array
|
||||
{
|
||||
[$gameId, $gameData] = explode(':', $line, 2);
|
||||
[$_, $gameId] = explode(' ', $gameId);
|
||||
|
||||
$hands = explode(';', $gameData);
|
||||
|
||||
return [$gameId, $hands];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, int>
|
||||
*/
|
||||
private function extractColors(string $hand): array
|
||||
{
|
||||
$colors = [];
|
||||
foreach (explode(',', $hand) as $part) {
|
||||
[$number, $color] = explode(' ', trim($part));
|
||||
$colors[$color] = (int) $number;
|
||||
}
|
||||
|
||||
return $colors;
|
||||
}
|
||||
|
||||
private function isValidHand(string $hand): bool
|
||||
{
|
||||
$lineData = ['red' => 0, 'green' => 0, 'blue' => 0];
|
||||
foreach (explode(',', $hand) as $part) {
|
||||
[$number, $color] = explode(' ', trim($part));
|
||||
$lineData[$color] += (int) $number;
|
||||
}
|
||||
|
||||
return !($lineData['red'] > self::$colorData['red']['max'] || $lineData['green'] > self::$colorData['green']['max'] || $lineData['blue'] > self::$colorData['blue']['max']);
|
||||
}
|
||||
}
|
159
data/Y23/day3/Day3.php
Normal file
159
data/Y23/day3/Day3.php
Normal file
@ -0,0 +1,159 @@
|
||||
<?php
|
||||
|
||||
namespace trizz\AdventOfCode\Y23;
|
||||
|
||||
use trizz\AdventOfCode\Solution;
|
||||
|
||||
final class Day3 extends Solution
|
||||
{
|
||||
public static null|int|string $part1ExampleResult = 4361;
|
||||
|
||||
public static null|int|string $part1Result = 539637;
|
||||
|
||||
public static null|int|string $part2ExampleResult = 467835;
|
||||
|
||||
public static null|int|string $part2Result = 82_818_007;
|
||||
|
||||
/**
|
||||
* @var string[][]
|
||||
*/
|
||||
private array $matrix = [];
|
||||
|
||||
#[\Override]
|
||||
public function part1(array $data): int
|
||||
{
|
||||
$this->createMatrix($data);
|
||||
$score = 0;
|
||||
|
||||
foreach ($this->matrix as $row => $line) {
|
||||
foreach (array_keys($line) as $col) {
|
||||
$numbers = $this->checkLocation($row, $col);
|
||||
$top = (int) $this->processNumbers($numbers['top'], sum: true);
|
||||
$bottom = (int) $this->processNumbers($numbers['bottom'], sum: true);
|
||||
|
||||
$score += $top + $bottom + max($numbers['left']) + max($numbers['right']);
|
||||
}
|
||||
}
|
||||
|
||||
return $score;
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function part2(array $data): int
|
||||
{
|
||||
$this->createMatrix($data);
|
||||
$score = 0;
|
||||
|
||||
foreach ($this->matrix as $row => $line) {
|
||||
foreach (array_keys($line) as $col) {
|
||||
if ($this->matrix[$row][$col] !== '*') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$numbers = $this->checkLocation($row, $col);
|
||||
|
||||
$top = (array) $this->processNumbers($numbers['top']);
|
||||
$bottom = (array) $this->processNumbers($numbers['bottom']);
|
||||
$left = max($numbers['left']);
|
||||
$right = max($numbers['right']);
|
||||
|
||||
$filteredResults = array_filter(array_values(array_merge_recursive($top, $bottom, [$left, $right])));
|
||||
|
||||
if (count($filteredResults) !== 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$score += array_product($filteredResults);
|
||||
}
|
||||
}
|
||||
|
||||
return (int) $score;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $numbers
|
||||
*
|
||||
* @return int|int[]
|
||||
*/
|
||||
private function processNumbers(array $numbers, bool $sum = false): array|int
|
||||
{
|
||||
$result = [];
|
||||
if ($numbers[0] !== 0 && $numbers[1] === 0 && $numbers[2] !== 0) {
|
||||
$result[] = $numbers[0];
|
||||
$result[] = $numbers[2];
|
||||
} else {
|
||||
$result[] = max($numbers);
|
||||
}
|
||||
|
||||
if ($sum) {
|
||||
return array_sum($result);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $data
|
||||
*/
|
||||
private function createMatrix(array $data): void
|
||||
{
|
||||
$this->matrix = array_map(static fn ($line): array => str_split((string) $line), $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, array<int,int>>
|
||||
*/
|
||||
private function checkLocation(int $row, int $col): array
|
||||
{
|
||||
$current = $this->matrix[$row][$col] ?? '.';
|
||||
if ($current === '.' || ctype_digit($current)) {
|
||||
return ['top' => [0, 0, 0], 'left' => [0], 'right' => [0], 'bottom' => [0, 0, 0]];
|
||||
}
|
||||
|
||||
$numbers = array_fill_keys(['top', 'left', 'right', 'bottom'], []);
|
||||
$positions = [
|
||||
'top' => [[$row - 1, $col - 1], [$row - 1, $col], [$row - 1, $col + 1]],
|
||||
'left' => [[$row, $col - 1]],
|
||||
'right' => [[$row, $col + 1]],
|
||||
'bottom' => [[$row + 1, $col - 1], [$row + 1, $col], [$row + 1, $col + 1]],
|
||||
];
|
||||
|
||||
foreach ($positions as $direction => $coords) {
|
||||
foreach ($coords as $coord) {
|
||||
$numbers[$direction][] = (int) $this->getNumber(...$coord);
|
||||
}
|
||||
}
|
||||
|
||||
return $numbers;
|
||||
}
|
||||
|
||||
private function getNumber(int $row, int $col, string $direction = null): ?string
|
||||
{
|
||||
$number = $this->matrix[$row][$col] ?? null;
|
||||
|
||||
if ($number === null || $number === '.' || !ctype_digit($number)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$toLeft = null;
|
||||
$toRight = null;
|
||||
|
||||
if ($direction === null || $direction === 'left') {
|
||||
$toLeft = $this->getNumber($row, $col - 1, 'left');
|
||||
}
|
||||
|
||||
if ($direction === null || $direction === 'right') {
|
||||
$toRight = $this->getNumber($row, $col + 1, 'right');
|
||||
}
|
||||
|
||||
if ($toLeft !== null) {
|
||||
$number = $toLeft.$number;
|
||||
}
|
||||
|
||||
if ($toRight !== null) {
|
||||
return $number.$toRight;
|
||||
}
|
||||
|
||||
return $number;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user