Refactor framework to support more years
This commit is contained in:
65
src/Y21/Day1.php
Normal file
65
src/Y21/Day1.php
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace trizz\AdventOfCode\Y21;
|
||||
|
||||
use trizz\AdventOfCode\Solution;
|
||||
|
||||
class Day1 extends Solution
|
||||
{
|
||||
public static int|string|null $part1ExampleResult = 7;
|
||||
public static int|string|null $part1Result = 1688;
|
||||
|
||||
public static int|string|null $part2ExampleResult = 5;
|
||||
public static int|string|null $part2Result = 1728;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
79
src/Y21/Day2.php
Normal file
79
src/Y21/Day2.php
Normal file
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace trizz\AdventOfCode\Y21;
|
||||
|
||||
use trizz\AdventOfCode\Solution;
|
||||
|
||||
class Day2 extends Solution
|
||||
{
|
||||
public static int|string|null $part1ExampleResult = 150;
|
||||
public static int|string|null $part1Result = 1654760;
|
||||
|
||||
public static int|string|null $part2ExampleResult = 900;
|
||||
public static int|string|null $part2Result = 1956047400;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
40
src/Y21/Day3.php
Normal file
40
src/Y21/Day3.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace trizz\AdventOfCode\Y21;
|
||||
|
||||
use trizz\AdventOfCode\Solution;
|
||||
|
||||
class Day3 extends Solution
|
||||
{
|
||||
public static int|string|null $part1ExampleResult = 198;
|
||||
public static int|string|null $part1Result = 3309596;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
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 $values) {
|
||||
$zeros = array_filter($values, static fn ($value) => $value === '0');
|
||||
$ones = array_filter($values, static fn ($value) => $value === '1');
|
||||
|
||||
$gammaRate .= ($ones > $zeros) ? '1' : '0';
|
||||
$epsilonRate .= ($ones < $zeros) ? '1' : '0';
|
||||
}
|
||||
|
||||
return (int) (bindec($gammaRate) * bindec($epsilonRate));
|
||||
}
|
||||
}
|
198
src/Y21/Day4.php
Normal file
198
src/Y21/Day4.php
Normal file
@ -0,0 +1,198 @@
|
||||
<?php
|
||||
|
||||
namespace trizz\AdventOfCode\Y21;
|
||||
|
||||
use trizz\AdventOfCode\Solution;
|
||||
|
||||
class Day4 extends Solution
|
||||
{
|
||||
public static int|string|null $part1ExampleResult = 4512;
|
||||
public static int|string|null $part1Result = 60368;
|
||||
|
||||
public static int|string|null $part2ExampleResult = 1924;
|
||||
public static int|string|null $part2Result = 17435;
|
||||
|
||||
/**
|
||||
* @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}
|
||||
*/
|
||||
public function part1(array $data): int|string
|
||||
{
|
||||
return $this->playBingo($data, firstWins: true);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public 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;
|
||||
}
|
||||
}
|
92
src/Y21/Day6.php
Normal file
92
src/Y21/Day6.php
Normal file
@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
namespace trizz\AdventOfCode\Y21;
|
||||
|
||||
use trizz\AdventOfCode\Solution;
|
||||
use JetBrains\PhpStorm\Immutable;
|
||||
|
||||
class Day6 extends Solution
|
||||
{
|
||||
public static int|string|null $part1ExampleResult = 5934;
|
||||
public static int|string|null $part1Result = 350917;
|
||||
|
||||
public static int|string|null $part2ExampleResult = 26984457539;
|
||||
public static int|string|null $part2Result = 1592918715629;
|
||||
|
||||
/**
|
||||
* @var int[]
|
||||
* @psalm-param array{int: int}
|
||||
*/
|
||||
#[Immutable]
|
||||
protected array $startState = [
|
||||
8 => 0,
|
||||
7 => 0,
|
||||
6 => 0,
|
||||
5 => 0,
|
||||
4 => 0,
|
||||
3 => 0,
|
||||
2 => 0,
|
||||
1 => 0,
|
||||
0 => 0,
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function part1(array $data): int
|
||||
{
|
||||
return $this->processPuzzle(80, $data[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function part2(array $data): int
|
||||
{
|
||||
return $this->processPuzzle(256, $data[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $state
|
||||
* @psalm-param array{int: int} $state
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected 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;
|
||||
}
|
||||
|
||||
protected function processPuzzle(int $numberOfDays, string $data): int
|
||||
{
|
||||
$state = $this->startState;
|
||||
|
||||
array_map(static function (string $stateValue) use (&$state) {
|
||||
++$state[(int) $stateValue];
|
||||
}, explode(',', $data));
|
||||
|
||||
for ($day = 0; $day < $numberOfDays; ++$day) {
|
||||
$state = $this->processDay($state);
|
||||
}
|
||||
|
||||
return (int) array_sum($state);
|
||||
}
|
||||
}
|
69
src/Y21/Day7.php
Normal file
69
src/Y21/Day7.php
Normal file
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace trizz\AdventOfCode\Y21;
|
||||
|
||||
use trizz\AdventOfCode\ExecuteDay;
|
||||
use trizz\AdventOfCode\Solution;
|
||||
|
||||
class Day7 extends Solution
|
||||
{
|
||||
public static int|string|null $part1ExampleResult = 37;
|
||||
public static int|string|null $part1Result = 344297;
|
||||
|
||||
public static int|string|null $part2ExampleResult = 168;
|
||||
public static int|string|null $part2Result = 97164301;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function part1(array $data): int
|
||||
{
|
||||
return $this->calculateFuel($data[0], forPart2: false);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function part2(array $data): int
|
||||
{
|
||||
return $this->calculateFuel($data[0], forPart2: true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $data
|
||||
* @param bool $forPart2
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function calculateFuel(string $data, bool $forPart2 = false): int
|
||||
{
|
||||
$crabs = array_map(static fn (string $crab) => (int) $crab, explode(',', $data));
|
||||
/** @psalm-param array{int: int} $fuelPerPosition */
|
||||
$fuelPerPosition = [];
|
||||
|
||||
for ($position = min($crabs); $position <= max($crabs); ++$position) {
|
||||
foreach ($crabs as $crab) {
|
||||
if (!isset($fuelPerPosition[$position])) {
|
||||
$fuelPerPosition[$position] = 0;
|
||||
}
|
||||
|
||||
$consumption = abs($position - $crab);
|
||||
|
||||
if ($forPart2) {
|
||||
$newConsumption = 0;
|
||||
// I'm sure there's another way than brute-forcing, but hey, this also works!
|
||||
for ($steps = 1; $steps <= $consumption; ++$steps) {
|
||||
$newConsumption += $steps;
|
||||
}
|
||||
|
||||
$consumption = $newConsumption;
|
||||
}
|
||||
|
||||
$fuelPerPosition[$position] += $consumption;
|
||||
}
|
||||
}
|
||||
|
||||
/** @psalm-suppress ArgumentTypeCoercion */
|
||||
return min($fuelPerPosition);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user