Part One: 2769675
Part Two: 24643097
See the Class
class Day01
{
/**
* @var array
*/
private array $set1 = [];
/**
* @var array
*/
private array $set2 = [];
/**
* @param string $file
*/
public function __construct($file)
{
if (! file_exists($file)) {
throw new RuntimeException('File not found');
}
$input = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
if ($input === false) {
throw new RuntimeException('Failed to read input file');
}
$this->processInput($input);
}
/**
* @param array $input
*/
private function processInput($input): void
{
foreach ($input as $line) {
$parts = preg_split('/\s+/', trim($line));
if ($parts !== false && count($parts) >= 2) {
[$v1, $v2] = $parts;
$this->set1[] = trim($v1);
$this->set2[] = trim($v2);
}
}
$this->set1 = array_map('trim', $this->set1);
$this->set2 = array_map('trim', $this->set2);
sort($this->set1);
sort($this->set2);
}
public function calculateSumPartOne(): int
{
return array_sum(array_map(function ($v1, $v2) {
return abs(intval($v1) - intval($v2));
}, $this->set1, $this->set2));
}
public function calculateSumPartTwo(): int
{
$sum = 0;
foreach ($this->set1 as $value) {
$occurrences = array_count_values($this->set2)[$value] ?? 0;
$sum += intval($value) * $occurrences;
}
return $sum;
}
}
class AdventOfCodeController extends Controller
{
private string $dataPath;
private Day01 $dayOne;
public function __construct()
{
$this->dataPath = base_path().'/path-to-data';
$this->dayOne = new Day01($this->dataPath.'/input_01.txt');
}
...
$this->dayOne->calculateSumPartOne(),
$this->dayOne->calculateSumPartTwo(),
...
}
Part One: 686
Part Two: 717
See the Class
class Day02
{
/**
* @var array>
*/
private array $report = [];
/**
* @param string $file
*/
public function __construct($file)
{
if (! file_exists($file)) {
throw new RuntimeException('File not found');
}
$input = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
if ($input === false) {
exit('Failed to read input file');
}
$this->processInput($input);
}
public function calculateSafe(bool $withSequence = false): int
{
$safe = 0;
foreach ($this->report as $v) {
if ($withSequence) {
if (! $this->isUnsafeWithSequence($v)) {
$safe++;
}
} else {
if (! $this->isUnsafe($v)) {
$safe++;
}
}
}
return $safe;
}
/**
* @param array $input
*/
private function processInput($input): void
{
foreach ($input as $line) {
if (empty(trim($line))) {
continue;
}
$v = explode(' ', trim($line));
$v = array_map('intval', $v);
$this->report[] = $v;
}
}
/**
* @param int[] $numbers
*/
private function isUnsafe(array $numbers): bool
{
$isIncreasing = $numbers[1] > $numbers[0];
for ($i = 0; $i < count($numbers) - 1; $i++) {
$diff = $numbers[$i + 1] - $numbers[$i];
if ($isIncreasing && $diff <= 0) {
return true;
}
if (! $isIncreasing && $diff >= 0) {
return true;
}
if (abs($diff) > 3 || $diff == 0) {
return true;
}
}
return false;
}
/**
* @param int[] $numbers
*/
private function isUnsafeWithSequence(array $numbers): bool
{
if (! $this->isUnsafeSequence($numbers)) {
return false;
}
for ($i = 0; $i < count($numbers); $i++) {
$tempArray = array_values(array_filter($numbers, function ($key) use ($i) {
return $key !== $i;
}, ARRAY_FILTER_USE_KEY));
if (! $this->isUnsafeSequence($tempArray)) {
return false;
}
}
return true;
}
/**
* @param int[] $numbers
*/
private function isUnsafeSequence(array $numbers): bool
{
if (count($numbers) < 2) {
return false;
}
$isIncreasing = $numbers[1] > $numbers[0];
for ($i = 0; $i < count($numbers) - 1; $i++) {
$diff = $numbers[$i + 1] - $numbers[$i];
if ($isIncreasing && $diff <= 0) {
return true;
}
if (! $isIncreasing && $diff >= 0) {
return true;
}
if (abs($diff) > 3 || $diff == 0) {
return true;
}
}
return false;
}
}
class AdventOfCodeController extends Controller
{
private string $dataPath;
...
private Day02 $dayTwo;
...
public function __construct()
{
$this->dataPath = base_path().'/path-to-data';
...
$this->dayTwo = new Day02($this->dataPath.'/input_02.txt');
...
}
...
$this->dayTwo->calculateSafe(),
$this->dayTwo->calculateSafe(true),
...
}
Part One: 153469856
Part Two: 77055967
See the Class
class Day03
{
private string $input;
/**
* @param string $file
*/
public function __construct($file)
{
if (! file_exists($file)) {
throw new RuntimeException('File not found');
}
$input = file_get_contents($file);
if ($input === false) {
exit('Failed to read input file');
} else {
$this->input = $input;
}
}
public function calculateSumPartOne(): int
{
$sum = 0;
preg_match_all('/mul\(\d{1,3},\d+\)/', $this->input, $matches);
foreach ($matches[0] as $match) {
preg_match('/mul\((\d+),(\d+)\)/', $match, $numbers);
if (isset($numbers[1]) && isset($numbers[2])) {
$sum += intval($numbers[1]) * intval($numbers[2]);
}
}
return $sum;
}
public function calculateSumPartTwo(): int
{
$sum = 0;
$shouldMultiply = true;
preg_match_all('/(?:mul\(\d+,\d+\)|do\(\)|don\'t\(\))/', $this->input, $matches);
foreach ($matches[0] as $operation) {
if ($operation === 'do()') {
$shouldMultiply = true;
continue;
}
if ($operation === "don't()") {
$shouldMultiply = false;
continue;
}
if (preg_match('/mul\((\d+),(\d+)\)/', $operation, $numbers)) {
if ($shouldMultiply) {
$sum += intval($numbers[1]) * intval($numbers[2]);
}
}
}
return $sum;
}
}
class AdventOfCodeController extends Controller
{
private string $dataPath;
...
private Day03 $dayThree;
...
public function __construct()
{
$this->dataPath = base_path().'/path-to-data';
...
$this->dayThree = new Day03($this->dataPath.'/input_03.txt');
...
}
...
$this->dayThree->calculateSumPartOne(),
$this->dayThree->calculateSumPartTwo(),
...
}
Part One: 2536
Part Two: 1875
See the Class
class Day04
{
private string $input;
/**
* @param string $file
*/
public function __construct($file)
{
if (! file_exists($file)) {
throw new RuntimeException('File not found');
}
$input = file_get_contents($file);
if ($input === false) {
exit('Failed to read input file');
} else {
$this->input = trim($input);
}
$this->processInput();
}
private function processInput(): void
{
$len = strpos($this->input, "\n") ?: strlen($this->input);
for ($i = 0; $i < strlen($this->input); $i += $len + 1) {
$line = substr($this->input, $i, $len);
if ($line) {
$this->lines[] = $line;
}
}
}
public function find(): int
{
$count = 0;
$cols = strlen($this->lines[0]);
$directions = [
[0, 1], // right
[0, -1], // left
[1, 0], // down
[-1, 0], // up
[1, 1], // diagonal down-right
[-1, -1], // diagonal up-left
[1, -1], // diagonal down-left
[-1, 1], // diagonal up-right
];
foreach ($this->lines as $row => $line) {
for ($col = 0; $col < $cols; $col++) {
foreach ($directions as $direction) {
if ($this->check($this->lines, 'XMAS', $row, $col, $direction[0], $direction[1])) {
$count++;
}
}
}
}
return $count;
}
/**
* @param string[] $lines
*/
private function check(array $lines, string $word, int $startRow, int $startCol, int $dirRow, int $dirCol): bool
{
$len = strlen($word);
for ($i = 0; $i < $len; $i++) {
$row = $startRow + $i * $dirRow;
$col = $startCol + $i * $dirCol;
if ($row < 0 || $row >= count($lines) || $col < 0 || $col >= strlen($lines[$row]) || $lines[$row][$col] !== $word[$i]) {
return false;
}
}
return true;
}
public function findX(): int
{
$count = 0;
$rows = count($this->lines);
$cols = strlen($this->lines[0]);
for ($row = 0; $row < $rows - 2; $row++) {
for ($col = 0; $col < $cols - 2; $col++) {
if ($this->checkX($this->lines, $row, $col)) {
$count++;
}
}
}
return $count;
}
/**
* @param string[] $lines
*/
private function checkX(array $lines, int $row, int $col): bool
{
$pos1a = $lines[$row][$col];
$pos1b = $lines[$row + 2][$col + 2];
$pos2a = $lines[$row][$col + 2];
$pos2b = $lines[$row + 2][$col];
if ($lines[$row + 1][$col + 1] !== 'A') {
return false;
}
if (
(($pos1a == 'M' && $pos1b == 'S') || ($pos1a == 'S' && $pos1b == 'M')) &&
(($pos2a == 'M' && $pos2b == 'S') || ($pos2a == 'S' && $pos2b == 'M'))
) {
return true;
}
return false;
}
}
class AdventOfCodeController extends Controller
{
private string $dataPath;
...
private Day04 $dayFour;
...
public function __construct()
{
$this->dataPath = base_path().'/path-to-data';
...
$this->dayFour = new Day04($this->dataPath.'/input_04.txt');
...
}
...
$this->dayFour->find(),
$this->dayFour->findX(),
...
}
Part One: 4766
Part Two: 6257
See the Class
class Day05
{
private string $input;
/**
* @var array|bool>
*/
private array $rules;
/**
* @var array>
*/
private array $updates;
/**
* @var array, array>
*/
private array $set1;
/**
* @var array, array>
*/
private array $set2;
/**
* @param string $file
*/
public function __construct($file)
{
if (! file_exists($file)) {
throw new RuntimeException('File not found');
}
$input = file_get_contents($file);
if ($input === false) {
exit('Failed to read input file');
} else {
$this->input = trim($input);
}
$this->processInput();
}
private function processInput(): void
{
$rules = $updates = [];
$lines = explode("\n\n", trim($this->input));
$rules = array_map(function ($line) {
return array_map('intval', explode('|', trim($line)));
}, explode("\n", trim($lines[0])));
$updates = array_map(function ($line) {
return array_map('intval', explode(',', trim($line)));
}, explode("\n", trim($lines[1])));
$this->rules = $rules;
$this->updates = $updates;
}
/**
* @param array|bool> $rules
*/
private function handleRules($rules): void
{
$this->rules = [];
foreach ($rules as $rule) {
if (is_array($rule) && isset($rule[0], $rule[1])) {
$this->rules[($rule[0] << 8) + $rule[1]] = true;
}
}
}
private function handleUpdates(): void
{
$sorted = [];
$corrected = [];
foreach ($this->updates as $update) {
if ($this->checkRules($update)) {
$sorted[] = $update;
} else {
usort($update, function ($a, $b) {
if (isset($this->rules[($a << 8) + $b])) {
return -1;
}
if (isset($this->rules[($b << 8) + $a])) {
return 1;
}
return 0;
});
$corrected[] = $update;
}
}
$this->set1 = $sorted;
$this->set2 = $corrected;
}
/**
* @param bool $corrected
*/
public function getSum($corrected = false): int
{
$this->handleRules($this->rules);
$this->handleUpdates();
if ($corrected) {
$set = $this->set2;
} else {
$set = $this->set1;
}
$sum = 0;
foreach ($set as $pages) {
$mid = floor((count($pages) - 1) / 2);
$sum += $pages[$mid];
}
return $sum;
}
/**
* @param array $update
*/
private function checkRules($update): bool
{
for ($i = 1; $i < count($update); $i++) {
$a = $update[$i - 1];
$b = $update[$i];
if (isset($this->rules[($b << 8) + $a])) {
return false;
}
}
return true;
}
}
class AdventOfCodeController extends Controller
{
private string $dataPath;
...
private Day05 $dayFive;
...
public function __construct()
{
$this->dataPath = base_path().'/path-to-data';
...
$this->dayFive = new Day05($this->dataPath.'/input_05.txt');
...
}
...
$this->dayFive->getSum(false),
$this->dayFive->getSum(true),
...
}
Part One: 4722
Part Two: 1602
See the Class
class Day06
{
/**
* @var array
*/
private array $input;
/**
* @var array
*/
private array $output;
/**
* @var array
*/
private array $originalInput;
/**
* @var array
*/
private array $guardPosition;
/**
* @var array
*/
private array $guardDirections;
/**
* @var array>
*/
private array $positions;
private string $currentDirection;
private int $rows;
private int $cols;
private bool $isExit;
private int $infiniteObstacles;
private ?int $maxMovements;
/**
* @param string $file
*/
public function __construct($file)
{
if (! file_exists($file)) {
throw new RuntimeException('File not found');
}
$input = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
if ($input === false) {
exit('Failed to read input file');
}
$this->input = $input;
$this->originalInput = $input;
$this->output = $input;
$this->processInput();
}
private function processInput(): void
{
$this->rows = count($this->input);
$this->cols = strlen($this->input[0]);
$this->guardPosition = $this->startingPosition();
$this->guardDirections = $this->guardDirections();
$this->currentDirection = '^';
$this->positions[] = $this->guardPosition;
$this->isExit = false;
$this->infiniteObstacles = 0;
$this->maxMovements = null;
}
/**
* @return array
*/
private function guardDirections(): array
{
return [
'^' => 'up',
'v' => 'down',
'<' => 'left',
'>' => 'right',
];
}
private function getDirection(string $direction): string
{
return $this->guardDirections[$direction];
}
/**
* @return array
*/
private function startingPosition(): array
{
for ($row = 0; $row < $this->rows; $row++) {
for ($col = 0; $col < $this->cols; $col++) {
if ($this->input[$row][$col] === '^') {
return [$row, $col];
}
}
}
return [];
}
/**
* @return array{
* guardPositions: int,
* output: array,
* positions: array>
* }
*/
public function predictGuardMovements(): array
{
while (! $this->isExit) {
$this->move($this->guardPosition[0], $this->guardPosition[1], $this->getDirection($this->currentDirection));
}
return [
'guardPositions' => count(array_unique($this->positions, SORT_REGULAR)),
'output' => $this->output,
'positions' => $this->positions,
];
}
public function move(int $row, int $col, string $direction): void
{
$checkBoard = $this->checkBoard($row, $col);
switch ($direction) {
case 'up':
if ($checkBoard[0] === 'exit') {
$this->exitBoard();
} elseif ($checkBoard[0] === '#') {
$this->move($row, $col, 'right');
} else {
$this->moveUp($row, $col);
}
break;
case 'down':
if ($checkBoard[1] === 'exit') {
$this->exitBoard();
} elseif ($checkBoard[1] === '#') {
$this->move($row, $col, 'left');
} else {
$this->moveDown($row, $col);
}
break;
case 'left':
if ($checkBoard[2] === 'exit') {
$this->exitBoard();
} elseif ($checkBoard[2] === '#') {
$this->move($row, $col, 'up');
} else {
$this->moveLeft($row, $col);
}
break;
case 'right':
if ($checkBoard[3] === 'exit') {
$this->exitBoard();
} elseif ($checkBoard[3] === '#') {
$this->move($row, $col, 'down');
} else {
$this->moveRight($row, $col);
}
break;
}
}
private function moveUp(int $row, int $col): void
{
$this->currentDirection = '^';
$this->update($row - 1, $col);
}
private function moveDown(int $row, int $col): void
{
$this->currentDirection = 'v';
$this->update($row + 1, $col);
}
private function moveLeft(int $row, int $col): void
{
$this->currentDirection = '<';
$this->update($row, $col - 1);
}
private function moveRight(int $row, int $col): void
{
$this->currentDirection = '>';
$this->update($row, $col + 1);
}
private function update(int $row, int $col): void
{
$this->positions[] = [$row, $col];
$this->guardPosition = [$row, $col];
$this->output[$row][$col] = 'X';
if ($this->maxMovements !== null) {
if ($this->isInfinite()) {
$this->infiniteObstacles++;
$this->exitBoard();
}
}
}
private function isInfinite(): bool
{
return $this->maxMovements !== null && count($this->positions) > $this->maxMovements;
}
private function exitBoard(): void
{
$this->isExit = true;
if ($this->maxMovements !== null) {
$this->positions = [];
}
}
/**
* @return array
*/
private function checkBoard(int $row, int $col): array
{
$up = $row == 0 ? 'exit' : $this->input[$row - 1][$col];
$down = $row == $this->rows - 1 ? 'exit' : $this->input[$row + 1][$col];
$left = $col == 0 ? 'exit' : $this->input[$row][$col - 1];
$right = $col == $this->cols - 1 ? 'exit' : $this->input[$row][$col + 1];
return [$up, $down, $left, $right];
}
private function addObstacle(int $row, int $col): bool
{
if ($this->input[$row][$col] === '.') {
$this->input[$row][$col] = '#';
return true;
}
return false;
}
private function resetBoard(): void
{
$this->input = $this->originalInput;
$this->output = $this->originalInput;
$this->guardPosition = $this->startingPosition();
$this->currentDirection = '^';
$this->positions = [];
$this->isExit = false;
}
private function checkCoordinates(int $row, int $col): string
{
if ($this->input[$row][$col] === '.') {
return 'empty';
}
if ($this->input[$row][$col] === '#') {
return 'obstacle';
}
if ($this->input[$row][$col] === '^') {
return 'guard';
}
return '';
}
public function createInfiniteLoops(): int
{
$predictedGuardMovements = $this->predictGuardMovements();
$positions = array_unique($predictedGuardMovements['positions'], SORT_REGULAR);
$this->maxMovements = count($predictedGuardMovements['positions']) * 2;
foreach ($positions as $position) {
if ($this->checkCoordinates($position[0], $position[1]) === 'empty') {
if ($this->addObstacle($position[0], $position[1])) {
$predictedGuardMovements = $this->predictGuardMovements();
$this->resetBoard();
}
}
}
return $this->infiniteObstacles;
}
}
class AdventOfCodeController extends Controller
{
private string $dataPath;
...
private Day06 $daySix;
...
public function __construct()
{
$this->dataPath = base_path().'/path-to-data';
...
$this->daySix = new Day06($this->dataPath.'/input_06.txt');
...
}
...
$this->daySix->predictGuardMovements()['guardPositions'],
$this->daySix->predictGuardMovements()['output'],
$this->daySix->createInfiniteLoops(),
...
}
..#.....................#............#....#...........#...............................#.....#.....................................
.................#............................#.............##..#...............................#....#..#.........#...............
.............................................##.........................................#....................#...............#...#
...##....................#...............#.....#....................#......##..............#............................#....#....
.................................#..#..............#...........#.............#..#........................#............#...........
.................#.........................#...........................#........................#........................#........
...#....................................#....#.........#................#...............#.....#................#.....#............
.............#.#..........................................................#...#..................#....#.......#.....#...#.........
.....#.......XXXXXXXXXXXXXXXXX#....##...#............................................................#............................
.............X....#...#......X......XXXXXXXXXXXXXXXXXXXXXXXXXXXX#..................#.............................#.#........#.....
......#......X...............X......X.#.#..........##..#.......X...#.........................#.......#....................#.......
.............X...............X...#..X...........#..............X#........#..........#.........#............#.........#.......#...#
..#..#.......X....#...#......X......X.......#...............#..X.........#.#........................#........#...............#....
.............X#..#.#.........X......X.......#.........#........X..##.......#..........#.....................#..............#...#..
.............X............#..X......X.......##.................X.................#....#.........#.................................
.............X....#..#.......X#....#X..............#...........X..#.......#.......#.............XXXXXXXXXXXXXXXXXXXXXXXXX#..#...#.
..........#.#X...............X#.....X...#...................#..X....................##......#...X................#......X#........
..........#..X......#...#....X......X...XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#X....#............#.....X..#......
...##........X....#..........X......X...X......................X..............#..#............X.X.....##.#...#.#..#.....X...#..#..
.....#......#X...............X......X...X.#.......#...#...#....X..........#.....#............#X.X.....#..#.#............X.........
........#...XXXXXXXXXXXXX#...X.....#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.X..........#..........#.X.........
.......#....XX..........X....X..........X.......#..........#...X...........................#..#.X.......................X.........
............XX....##....X..#.X.....#....X#............#.......#X..#.........#....##.............X.#..#.............#....X.....#...
.......#....XX.....#....X..XXXX#........X..................#...X.........#.........#..#.........X..............#.##.....X.........
............XX..........X..X.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#........#....#............X.......................X......#..
....#.......XX..#......#X..X.XXX.#......X......................X....X.#.........................X.......#......#........X.........
...#........XX..........X..X.XXX........X...#..#...............X....X.XXXXXXXXX#................X....#.#......#.........X.........
..#.....#...XX..........X..X.XXX........X.#.................#..X.#..X.X.......X.................X.............#......#..X.........
..XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX##..........X#...X.X.#.....X.......#.........X.......#.......#.......X.........
..X.........XX..........X..X.XXX........X.........X.......##...X....X.X...#...X#....#...........X.#.....................X.........
..X.........XX..........X..X.XXX........X.........X.....#..XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#.X.......................X......#..
..X.........XX##....#..#X..X.XXX..#.....X.....#...X..#..#..X.#.X....X#X.......X.......#......X..X..................#..#.X.#.......
..X.........XX..........X.#X.XXX........X......#..X........X...X....X.X.......X..............X..X#......................X.........
#.X.........XX..........X..X.XXX........X........#X........X...X....X.X#..#...X...........##.X..X...#...#...#.........#.X.........
..X........#XXXXXXXXXXXXXXXXXXXX........X....#..#.X........X...X....X#X#......X#.............X..X.........#.......#.....X.#.......
..X..........X..........X..X.X#X.....#..X...#.....X........X..#X....X.X.......X.#............X..X.#.#.............#..#.#X.........
..X..........X.........#X..X.X.X........X...#.....X........X...X....X.X.......X..#.....#.#...X..X#......................X.....#...
..X..........X..........X..X.X.X........X...XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#X#......................X.........
#.X..........X.....#....X#.X.X.X........X...X.....X......#.X...X....X.X.#...#.X..............XX.X.......................X.........
..X..........X..........XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#X...X....X.X.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#...........X.........
..X.......#..X..........XX.X.X.X....#...X...X.....X......X.X...X....X.X.X.....X...........#..XX.X...#......X...#........X.......#.
..X.....#....X.........#XX#X.X.X........X...X.....X......X.X...X.#..X.X.X..#..X.........#....XX.X..........X............X.#.......
..X....#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#..X.X.X..#..X.............#XX.X..........X............X...#.....
..X.....X..#.X..........XX.X.X.X........X...X.....X.....#X.X#..XX...X.X.X.....X...........#..XX.^..........X............X........#
..X.....X..XXXXX#.......XX.X.X#X..#.....X...X.....X......X.X...XX#..X.X.X.....X..............XX.....#......X............X.........
..X.....X..X.X.X........XX.X.X.X..XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#.X............X.........
..X.....X..X.X.X........XX.X.X.X..X.....X...X....#X......X#X...XX...X.X.X.....X...#.#.....#..XX.........X..X.....#......X.........
..X.....X#.X.X.X...#...#XX.X.X.X..X.....X...X....XXXXXXXXXXXXXXXXXXXXXXXXXXX#.X..............XX.........X..X............X.........
##X##..#X..X.X.X........XX.X.X.X..X.....X..#X....XX......X.X...XX...X.X.X..X..X..............XX.........X..X............X.........
..X.....X..X.X.X.#......XX.X.X.X..X.....X..XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#........#.X.........
..X.....X..X.X.X........XX.X.X.X..X.....X..XX....XX......X.X...XX...X.X.X#.X..X..............XX...#....#X..XX...........X.....#...
..X.....X..X.X#X........XX.X.X.X..X..#..X..XX.#..XX......X.X...XX...X.X.X#.X..X....#.........XX.........X..XX...........X.........
..X.....X..X.X.X.......#XX.X.X.X..X.....X..XX.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX##..X..XX...........X.........
..X..#..X..X.X.X.......#XX.X.X.X#.X.#...X..XX.X..XX.#....X.X...XX...X.X.X..X..X...#..........XX....X....X#.XX...........X.........
..X.#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#....XX....X.##.X..XX...........X.........
..X..X..X..X.X.X#.......XX.X.X.X..X.....X..XX#X..XX......X.X...XX...X.X.X..X..X........X..#..XX....X....X..XX...........X.........
..X..X..X..X.X.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#X..XX.#.........X.........
..X..X..X..X.X.XX.......XX.X.X.X..X.....X..XX.X..XX......X.X...XX...X.X.X..X..X.......#X..#..XX....X..X.X..XX...........X.........
..X..X..X..X.X.XX.......XX.X.X.X..X.....X..XX.X#.XX.....#X.X...XX..#X.X.X..X..X........X.....XX.#..X..X.X..XX..#.......#X.........
..X..X..X..X.X#XX.......XX.X.X.X..X.#...X..XX#X.#XX......X.X...XX...X.X.X..X.#X..#..#.#X.....XX....X..X.X..XX..XXXXXXXXXXX#.......
..X..X..X.#XXXXXXXXXXXXXXX.X.X.X..X.....X.#XX.X..XX......X.X...XX...X.X#X..X..X........X.....XX....X..X.X..XX..X...#....XX........
..X.#X..X....X.XX#.#....#X.X.X.X..X.....X..XX.X..XX......X.X...XX...X.X.X..X..X........X.....XX#...X..X.X..XX..X........XX........
..X..X..X....X.XX......#.X.X.X.X..X.....X..XX.X#.XX......X.X...XX...X.X.X..X..X.#......X.....XX#...X..X.X..XX..X.....#..XX.....#..
..X..X..X..#.X.XX........X.X.X.X..X..#..X..XX.X..XX#.#...X.X..#XX...X.X#X..X..X........X.....XX....X..X.X..XX..X..#.....XX........
..X..X.#X....X.XX........X.X.X#X..X.....X..XX.X..XX#.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#...X.....XX....X..X.X.#XX..X......#.XX........
..X..X..X...#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX..X........XX........
..X.#X..X...#..XX........X.X.X.X..X.....X..XX.X..XX..X...X.X...XX...X.X.X#.X..X...X#...X.....XX..#.X..X.X..X#..X.#......XX..#.....
..X..X.#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX..#X....X.....XX....X#.X#X..X#..X....#...XX........
..X..X.........XX........X.X#X.X.#X.....X#.XX.X.#XX..X..#X.X...XX...X.X.X..X..#...X....X.....XX.#..X..X.X..X...X........XX........
..X..X.........XX........X.XXXXXXXXXXXXXXXXXXXXXXXXXXXX#.X.X...XX...X#XXXXXX......X....X.....XX.#..X..X.X..X...X........XX........
..X..X.........XX........X.XXX.X..X.#...X..XX.X..XX..XX..X.X...XX...X...X.##......X....X.....XX...#X..X.X..X...X........XX.....#..
..X..X........#XX........X.XXX#X..X.....X..XX.X..XX#.XX..X.X...XX...X...X..#......X....X....#XX....X..X.X..X...X...#....XX........
..X..X.........XX...#....X.XXX.X..X.....X..XX.X.#XX..XX..X.X...XX...X...X..XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#...XX........
..X#.X.........XX........X.XXX#X..X.....X..XX.X..XX..XX..X#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX..X.X..X...X...X...#XX........
..X..X........#XX........X.XXX.X#.X.....X..XX.X..XX..XX..X....#XX#..X...X#.X......X....X.....XX..#.##.X.X..X...X...X....XX........
..X..X.........XX...#....X#XXX#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX...X...X..X.#....X.#..X.....XX.#.....X.X..X...X..#X.#..XX#.......
#.X..X.........XX...XXXXXXXXXXXXXXXXXXX#X..XX.X..XX..XX..X...#.X#..#X#..X..X.....#X....X.....XX#......X.X..X...X...X....XX.......#
..X..X.........XX...X#...X.XXX....X...X.X..XX.X..XX..XX..X.....X....X...X..X.....#X....X....#XX.......X.X##X...X...X.#..XX........
..X..X.#.......XX...X....X.XXX....X...X.X.#XX.X..XX..XX..X#....X....X#..X.#X......X....X.....XX.......X.X.XXXXXXXXXXXXXXXXXXXX#...
..X..X.......#.XX...X....X.XXX.#..X...X.X..XX.X..XX..XX..X.#...X....X...X.#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX...X...X....XX..#X....
..X..X.........XX...X....X.XXX.#..X...X.X..XX.X..XX..XX..X.....X....X.##X.........X....X.....XX.......X.X.X#.#.X...X#...XX...X.#..
..X..X.........XX##.X....X.XXX....X...X#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX....X.#...XX.......X.X.X....X#..X....XX...X...#
..X..X..#......XX...X....X.XXX....X...X....XX#X..XX..XX.#X.....X....X...X.........#....X.....XX......#X.X.X....X...X....XX..#X....
..X#.X..#......XX...X....X.XXX....X.#.X....XX.X..XX..XX.XXXXXXXXXXX#X#..X..............X.....XX.#.....X.X.X....X...X....XX...X....
.#X..X.........XX...X....X.XXX....X..#X.#..XX.X#.XX..XX.XX.....X..X.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#.X....XX...X....
..X..X.........XX...X....X.XXX...#X...X....XX#X..XX..XX.XX....#X#.X.XX..X..............X.#...XX.......X.X.X....XX..X....XX...X##..
#.X#.X...##....XX...X....X#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX...#.XX.#.....X#X.X....XX..X....XX...X...#
.#X..X#...XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#.X..X.XX..X..............#.....XX.......X.X.X....XX..X....XX...X.#..
.#XXXXXXXXXXXXXXX...X....X..XX....X...X....XX.X.#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.......X.X.X....XX.#X....XX...X....
.....X....X....#X...X....X..XX....X...X....XX.X...X..XX.XX#.X.#X..X.XX..X....................X#.#..#.#X.X.X...#XXXXXXXXXXXXXXX....
.....X....X...#.X...X....X..XX....X...X....XX.X#..X..XX#XX..X..X..X.XX..X.................#..X........X.X.X.....X..X#...XX...#....
.....X....X.....X...X..##XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX..X.XX..X..#.................X........X.X.X.....X..X....XX........
.....X....X.....X...X.......XX#...X...X....XX.X...X..XX.XX..X.##..X.XX..X....................X........X.X.X.....X..X....XX........
#....X....X.....X...X#......XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#...........X........X.X.X....#X..X....XX........
#....X....X.....X...X.......XXX...X...X....XX.X...X..XX.XX..X.....X#XX.#X.......X............X........X.X.X.....X..X....XX........
#..#.X....X.#...X...X.......XXX...X...X....XX.X.#.X#.XX.XX..X.##..X.XX..X.....#.X............X........X.X.X.....X..X....XX........
.....X....X.....X...X..#....XXX...X...X....XX#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX........
.....X....X.....X...X#......XXX..#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.#......X.X.X.....X..X....#X...#....
.....X....X....#X...X.......XXX.......X....XX.....X..XX.XX..X.....X.XX..X.......X...........##...#....X.X.X.....X..X.....X..#.....
...#.X....X.....X...X.......XXX.......X...#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX........
....#X....X.....X...X.......XXX.#.....X...#.X.....X..XX.XX..X.....X.XX..X.......X...........#.........X#X.X#....X..X#....#........
.....X#...X#.#..X...X.......XXX.......X...#.X.....X..XX.XX..X.....X.XX..X.......X....#................X.X.X.....X..X..........#...
.....X....X#...#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX..X#....X.XX..X.......X...#.................X#X.X.....X..X.........#....
...#.X....X...#XXXXXXXX#....XXX...#...X...#.X.....X..XX.X#..X.....X.XX..X.......X......#..............X.X.X....#X..X............#.
.....X....X#...X....X.X....#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX..X.......X......#..............X.X.X.....X..X..............
..#..X....X..##XXXXXXXXXXXXXXXXXXXXXXXX.....X.....X..XX.X...X...#.X.#X..X.......X...#.................X.X.X.#...X..X........##....
.....X....X.........X.X#.....XX.......#.....X.....X.#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.X#....X..X..........#...
.....X....X...#..#..X.X...##.XX.......#.....X.....X...X.X...X.....X..X..X.......X.........#...........X.##XXXXXXX..X..........#...
.....X....X........#X.X......XX.............X.....X...X.X...X.....X..X#.X.......X......#......#.....#.X.......#.#..X#.............
.....X....X.......#.X.X......XX.............X.....X...X.X...X.....X.#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX..#.........#.
.....X....X.........X.X......XX.......#.#...X....#X...X#X..#X.....X...#.X.......X....#................X........#...#..............
.....X....X.........X.X......XX..#..........X...#.X#..X.X...X.....X.....X#......X.........#...........X.#..#..#...................
.....X...#X......#.#XXXXXXXXXXX...#..#......X.....X..#X.X...X.....X....#X.......X.....................X...#..#....................
.....X....X...........X......#X...#.......##XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.......................##..
.....X....X...........X.......X............XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#........#.......#......#....................
.....X..#.X.........#.X.......X#..........#X......X...X.X...X.....X....#X.......X...X..#........#.#...........................#...
.#...X....X........#..X#......X.......#...#XXXXXXXXXXXX.X...X.#...X....#X.......X...X..#..........................................
.....X...#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX...##XXXXX...#.X....#XXXXXXXXX...X........#.......................#............
.....X................X.......X........#..........#..#......#.....X..#..........#...X......................#....................#.
....#X.....#..........X.......X.......#....................#......X.............XXXXXXXXXXXXXXXXXXX#.....###.......#..............
.....X.#..............X...#...X#......XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#..........................
....#XXXXXXXXXXXXXXXXXX......#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX......#......X#..X.......................#...
......................#...............X.................#.........X#............X...#..#..........X...X..........#................
...........#..........................X.........................#.X............#XXXXXXXXXXXXXXXXXXXXXXX....#......................
.....#................#........#...#..X.#........#............#...X...........#...................X...#..#.........#..............
#.....................................X............#..............X............#....#...........#.X...............................
....#.#...............................X.....#.#...............#...X............#....XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#.............
............#....................#.#.#XXXXXXXXXXXXXXXXXXXXXXXXXXXXX.................X#............X......##........X..............
...##..............#........................#.............#.......#...........#.#.##XXXXXXXXXXXXXXX...............#X...##.........
......#...........#....................................................#.........................##................X..............
Part One: 2501605301465
Part Two: 44841372855953
See the Class
class Day07
{
private string $input;
private array $equations = [];
public function __construct($file)
{
if (! file_exists($file)) {
throw new RuntimeException('File not found');
}
$this->input = file_get_contents($file);
$this->processInput();
}
private function processInput(): void
{
$lines = explode("\n", trim($this->input));
foreach ($lines as $line) {
if (preg_match('/^(\d+): (.*)$/', $line, $matches)) {
$this->equations[] = [
'target' => (int) $matches[1],
'numbers' => array_map('intval', explode(' ', $matches[2])),
];
}
}
}
private function evaluate(array $numbers, array $operators, bool $includeConcatenation = false): int
{
$result = $numbers[0];
for ($i = 0; $i < count($operators); $i++) {
switch ($operators[$i]) {
case '+':
$result += $numbers[$i + 1];
break;
case '*':
$result *= $numbers[$i + 1];
break;
case '.':
if ($includeConcatenation) {
$result = (int) ($result.$numbers[$i + 1]);
}
break;
}
}
return $result;
}
private function isTarget(array $equation, bool $includeConcatenation = false): bool
{
$numbers = $equation['numbers'];
$target = $equation['target'];
$operatorCount = count($numbers) - 1;
$combinations = pow(3, $operatorCount);
for ($i = 0; $i < $combinations; $i++) {
$operators = [];
$temp = $i;
for ($j = 0; $j < $operatorCount; $j++) {
// Use modulo to get each operator
switch ($temp % 3) {
case 0:
$operators[] = '*';
break;
case 1:
$operators[] = '+';
break;
case 2:
if ($includeConcatenation) {
$operators[] = '.';
}
break;
}
$temp = (int) ($temp / 3);
}
if ($this->evaluate($numbers, $operators, $includeConcatenation) === $target) {
return true;
}
}
return false;
}
public function getResult(bool $includeConcatenation = false): int
{
$sum = 0;
foreach ($this->equations as $equation) {
if ($this->isTarget($equation, $includeConcatenation)) {
$sum += $equation['target'];
}
}
return $sum;
}
}
class AdventOfCodeController extends Controller
{
private string $dataPath;
...
private Day07 $daySeven;
...
public function __construct()
{
$this->dataPath = base_path().'/path-to-data';
...
$this->daySeven = new Day07($this->dataPath.'/input_07.txt');
...
}
...
$this->daySeven->getResult(false),
$this->daySeven->getResult(true),
...
}
Part One: 244
Part Two: 912
See the Class
class Day08
{
/**
* @var array
*/
private array $input;
/**
* @param string $file
*/
public function __construct($file)
{
if (! file_exists($file)) {
throw new RuntimeException('File not found');
}
$input = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
if ($input === false) {
exit('Failed to read input file');
}
$this->input = $input;
$this->processInput();
}
private function processInput(): void
{
$this->input = array_map(function ($row) {
if (is_string($row)) {
return str_split($row);
}
return $row;
}, $this->input);
}
/**
* @return array>>
*/
public function findAntennas(): array
{
$antennas = [];
/**
* @var array $line
*/
foreach ($this->input as $y => $line) {
foreach ($line as $x => $char) {
if ($char !== '.') {
if (! isset($antennas[$char])) {
$antennas[$char] = [];
}
$antennas[$char][] = [$x, $y];
}
}
}
return $antennas;
}
/**
* @param array>> $antennas
* @return array>
*/
private function calculateAntinodesPart2(array $antennas): array
{
$antinodePositions = [];
if (! is_array($this->input) || empty($this->input) || ! is_array($this->input[0])) {
return [];
}
$gridWidth = count($this->input[0]);
$gridHeight = count($this->input);
foreach ($antennas as $freq => $positions) {
$n = count($positions);
if ($n < 2) {
continue;
}
for ($i = 0; $i < $n; $i++) {
for ($j = $i + 1; $j < $n; $j++) {
[$x1, $y1] = $positions[$i];
[$x2, $y2] = $positions[$j];
$dx = $x2 - $x1;
$dy = $y2 - $y1;
$gcd = $this->gcd(abs($dx), abs($dy));
if ($gcd > 0) {
$stepX = $dx / $gcd;
$stepY = $dy / $gcd;
$minSteps = -max(
ceil($x1 / abs($stepX ?: 1)),
ceil($y1 / abs($stepY ?: 1))
);
$maxSteps = max(
ceil(($gridWidth - $x1) / abs($stepX ?: 1)),
ceil(($gridHeight - $y1) / abs($stepY ?: 1))
);
for ($k = $minSteps; $k <= $maxSteps; $k++) {
$x = $x1 + ($k * $stepX);
$y = $y1 + ($k * $stepY);
if ($x >= 0 && $x < $gridWidth &&
$y >= 0 && $y < $gridHeight &&
$x == (int) $x && $y == (int) $y) {
$antinodePositions[] = [(int) $x, (int) $y];
}
}
}
}
}
foreach ($positions as $pos) {
$antinodePositions[] = $pos;
}
}
return array_unique($antinodePositions, SORT_REGULAR);
}
private function gcd(int $a, int $b): int
{
$a = abs($a);
$b = abs($b);
while ($b !== 0) {
$temp = $b;
$b = $a % $b;
$a = $temp;
}
return $a;
}
/**
* @param array>> $antennas
* @return array>
*/
public function calculateAntinodesPart1(array $antennas): array
{
$antinodePositions = [];
foreach ($antennas as $freq => $positions) {
$n = count($positions);
for ($i = 0; $i < $n; $i++) {
for ($j = 0; $j < $n; $j++) {
if ($i !== $j) {
[$x1, $y1] = $positions[$i];
[$x2, $y2] = $positions[$j];
$dx = $x2 - $x1;
$dy = $y2 - $y1;
$antinode1 = [$x1 - $dx, $y1 - $dy];
$antinode2 = [$x2 + $dx, $y2 + $dy];
$antinodePositions[] = $antinode1;
$antinodePositions[] = $antinode2;
}
}
}
}
return $antinodePositions;
}
public function countUniqueAntinodes(bool $part2 = false): int
{
$antennas = $this->findAntennas();
if ($part2) {
$antinodePositions = $this->calculateAntinodesPart2($antennas);
} else {
$antinodePositions = $this->calculateAntinodesPart1($antennas);
}
if (! is_array($this->input) || empty($this->input) || ! is_array($this->input[0])) {
return 0;
}
$uniqueAntinodes = [];
foreach ($antinodePositions as [$x, $y]) {
if ($x >= 0 && $x < count($this->input[0]) && $y >= 0 && $y < count($this->input)) {
$uniqueAntinodes["$x,$y"] = true; // Use associative array to ensure uniqueness
}
}
return count($uniqueAntinodes);
}
}
class AdventOfCodeController extends Controller
{
private string $dataPath;
...
private Day08 $dayEight;
...
public function __construct()
{
$this->dataPath = base_path().'/path-to-data';
...
$this->dayEight = new Day08($this->dataPath.'/input_08.txt');
...
}
...
$this->dayEight->countUniqueAntinodes(false);
$this->dayEight->countUniqueAntinodes(true),
...
}
......#....#
...#....0...
....#0....#.
..#....0....
....0....#..
.#....#.....
...#........
#......#....
........A...
.........A..
..........#.
..........#.
Part One: 6384282079460
Part Two: 6408966547049
See the Class
class Day09
{
private string $input;
/**
* @var array
*/
private array $diskMap;
/**
* @param string $file
*/
public function __construct($file)
{
if (! file_exists($file)) {
throw new RuntimeException('File not found');
}
$input = file_get_contents($file);
if ($input === false) {
exit('Failed to read input file');
} else {
$this->input = $input;
}
$this->processInput();
}
private function processInput(): void
{
$this->diskMap = $this->formatDiskMap();
}
/**
* @return array
*/
private function formatDiskMap(): array
{
$formattedDiskMap = [];
$i = 0;
$totalLength = 0;
foreach (str_split($this->input) as $n => $item) {
$count = (int) $item;
$totalLength += $count;
}
$formattedDiskMap = array_fill(0, $totalLength, '.');
$currentIndex = 0;
foreach (str_split($this->input) as $n => $item) {
$count = (int) $item;
if ($n % 2 === 0) {
for ($j = 0; $j < $count; $j++) {
$formattedDiskMap[$currentIndex++] = (string) $i;
}
$i++;
} else {
$currentIndex += $count;
}
}
return $formattedDiskMap;
}
private function freeDiskSpace(): void
{
$length = count($this->diskMap);
$rightmostNumPos = -1;
for ($i = 0; $i < $length; $i++) {
if (is_numeric($this->diskMap[$i])) {
$rightmostNumPos = $i;
}
}
while ($rightmostNumPos > 0) {
$leftmostDotPos = -1;
for ($i = 0; $i < $rightmostNumPos; $i++) {
if ($this->diskMap[$i] === '.') {
$leftmostDotPos = $i;
break;
}
}
if ($leftmostDotPos !== -1 && $leftmostDotPos < $rightmostNumPos) {
$this->diskMap[$leftmostDotPos] = $this->diskMap[$rightmostNumPos];
$this->diskMap[$rightmostNumPos] = '.';
$rightmostNumPos--;
while ($rightmostNumPos >= 0 && ! is_numeric($this->diskMap[$rightmostNumPos])) {
$rightmostNumPos--;
}
} else {
break;
}
}
}
public function checkSum(): int
{
$this->freeDiskSpace();
return array_sum(array_map(
fn ($k, $v) => $v === '.' ? 0 : (int) $k * (int) $v,
array_keys($this->diskMap),
$this->diskMap
));
}
private function processDiskMap(string $input): void
{
$this->diskMap = [];
$id = 0;
for ($i = 1; $i <= strlen($input); $i++) {
$value = intval($input[$i - 1]);
for ($j = 0; $j < $value; $j++) {
if ($i % 2 == 0) {
$this->diskMap[] = -1;
} else {
$this->diskMap[] = $id;
}
}
if ($i % 2 != 0) {
$id++;
}
}
}
public function calculateChecksum(): int
{
$this->processDiskMap($this->input);
$diskMap = $this->diskMap;
$id = count(array_unique(array_filter($diskMap, fn ($x) => $x >= 0))) - 1;
$maxIdIdx = count($diskMap);
while ($id >= 0) {
while ($diskMap[$maxIdIdx - 1] != $id) {
$maxIdIdx--;
}
$minIdIdx = $maxIdIdx - 1;
while ($minIdIdx > 0 && $diskMap[$minIdIdx - 1] == $id) {
$minIdIdx--;
}
$idSize = $maxIdIdx - $minIdIdx;
$minEmptySlotIdx = 0;
while (true) {
while ($minEmptySlotIdx < count($diskMap) && $diskMap[$minEmptySlotIdx] != -1) {
$minEmptySlotIdx++;
}
$maxEmptySlotIdx = $minEmptySlotIdx + 1;
while ($maxEmptySlotIdx < count($diskMap) && $diskMap[$maxEmptySlotIdx] == -1) {
$maxEmptySlotIdx++;
}
$emptySlotSize = $maxEmptySlotIdx - $minEmptySlotIdx;
if ($emptySlotSize >= $idSize) {
break;
}
$minEmptySlotIdx += $emptySlotSize;
if ($minEmptySlotIdx >= $minIdIdx) {
break;
}
}
if ($minEmptySlotIdx < $minIdIdx) {
for ($i = 0; $i < $idSize; $i++) {
$diskMap[$minEmptySlotIdx + $i] = $id;
}
for ($i = $minIdIdx; $i < $maxIdIdx; $i++) {
$diskMap[$i] = -1;
}
}
$id--;
}
$diskMap = array_map(fn ($x) => $x == -1 ? 0 : $x, $diskMap);
$checksum = 0;
foreach ($diskMap as $i => $v) {
$checksum += (int) $i * (int) $v;
}
return $checksum;
}
}
class AdventOfCodeController extends Controller
{
private string $dataPath;
...
private Day09 $dayNine;
...
public function __construct()
{
$this->dataPath = base_path().'/path-to-data';
...
$this->dayNine = new Day09($this->dataPath.'/input_09.txt');
...
}
...
$this->dayNine->checkSum(),
$this->dayNine->calculateChecksum(),
...
}
Part One: 794
Part Two: 1706
See the Class
class Day10
{
/**
* @var array>
*/
private array $input;
private int $totalScore = 0;
/**
* @param string $file
*/
public function __construct($file)
{
if (! file_exists($file)) {
throw new RuntimeException('File not found');
}
$input = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
if ($input === false) {
throw new RuntimeException('Failed to read input file');
}
$this->processInput($input);
}
/**
* @param array $input
*/
private function processInput($input): void
{
foreach ($input as $row) {
$this->input[] = str_split($row);
}
}
public function calculateTrailheadScores(): int
{
$rows = count($this->input);
$cols = count($this->input[0]);
for ($row = 0; $row < $rows; $row++) {
for ($col = 0; $col < $cols; $col++) {
if ($this->input[$row][$col] === '0') {
$this->totalScore += $this->countNinesFromTrailhead($row, $col);
}
}
}
return $this->totalScore;
}
private function countNinesFromTrailhead(int $startRow, int $startCol): int
{
$visited = [];
$stack = [[$startRow, $startCol]];
$countNines = 0;
while (! empty($stack)) {
[$row, $col] = array_pop($stack);
if ($this->input[$row][$col] === '9') {
$countNines++;
}
$visited["$row,$col"] = true;
foreach ([[-1, 0], [1, 0], [0, -1], [0, 1]] as [$dRow, $dCol]) {
$newRow = $row + $dRow;
$newCol = $col + $dCol;
if ($this->isValidPosition($newRow, $newCol, $visited, $this->input[$row][$col])) {
$stack[] = [$newRow, $newCol];
}
}
}
return $countNines;
}
/**
* @param array $visited
*/
private function isValidPosition(int $row, int $col, array $visited, string $currentHeight): bool
{
if ($row < 0 || $row >= count($this->input) || $col < 0 || $col >= count($this->input[0])) {
return false;
}
if (isset($visited["$row,$col"])) {
return false;
}
return (int) $this->input[$row][$col] === (int) $currentHeight + 1;
}
private function countDistinctTrails(int $startRow, int $startCol): int
{
return $this->findPaths($startRow, $startCol, []);
}
/**
* @param array|string> $path
*/
private function findPaths(int $row, int $col, array $path): int
{
$path[] = "$row,$col";
if ($this->input[$row][$col] === '9') {
return 1;
}
$paths = 0;
$currentHeight = (int) $this->input[$row][$col];
foreach ([[-1, 0], [1, 0], [0, -1], [0, 1]] as [$dRow, $dCol]) {
$newRow = $row + $dRow;
$newCol = $col + $dCol;
if ($this->isValidNextPosition($newRow, $newCol, $currentHeight) &&
! in_array("$newRow,$newCol", $path)) {
$paths += $this->findPaths($newRow, $newCol, $path);
}
}
return $paths;
}
private function isValidNextPosition(int $row, int $col, int $currentHeight): bool
{
if ($row < 0 || $row >= count($this->input) || $col < 0 || $col >= count($this->input[0])) {
return false;
}
return (int) $this->input[$row][$col] === $currentHeight + 1;
}
public function calculateTrailheadRatings(): int
{
$rows = count($this->input);
$cols = count($this->input[0]);
$totalRatings = 0;
for ($row = 0; $row < $rows; $row++) {
for ($col = 0; $col < $cols; $col++) {
if ($this->input[$row][$col] === '0') {
$totalRatings += $this->countDistinctTrails($row, $col);
}
}
}
return $totalRatings;
}
public function getTotalTrailheadRatings(): int
{
return $this->calculateTrailheadRatings();
}
}
class AdventOfCodeController extends Controller
{
private string $dataPath;
...
private Day10 $dayTen;
...
public function __construct()
{
$this->dataPath = base_path().'/path-to-data';
...
$this->dayTen = new Day10($this->dataPath.'/input_10.txt');
...
}
...
$this->dayTen->calculateTrailheadScores(),
$this->dayTen->getTotalTrailheadRatings(),
...
}
Part One: 189092
Part Two: 224869647102559
See the Class
class Day11
{
private string $input;
/**
* @var array
*/
private array $stones;
/**
* @param string $file
*/
public function __construct($file)
{
if (! file_exists($file)) {
throw new RuntimeException('File not found');
}
$input = file_get_contents($file);
if ($input === false) {
exit('Failed to read input file');
} else {
$this->input = $input;
}
$this->processInput();
}
private function processInput(): void
{
$this->stones = array_map('intval', preg_split('/\s+/', trim($this->input)) ?: []);
}
public function simulateBlinks(int $blinks): int
{
$stones = $this->stones;
$stoneCounts = array_count_values($stones);
for ($blink = 0; $blink < $blinks; $blink++) {
$newStoneCounts = [];
foreach ($stoneCounts as $stone => $count) {
if ($stone === 0) {
$newStoneCounts[1] = ($newStoneCounts[1] ?? 0) + $count;
} elseif ($this->hasEvenDigits($stone)) {
$digits = strlen((string) $stone);
$half = (int) ($digits / 2);
$left = (int) substr((string) $stone, 0, $half);
$right = (int) substr((string) $stone, $half);
$newStoneCounts[$left] = ($newStoneCounts[$left] ?? 0) + $count;
$newStoneCounts[$right] = ($newStoneCounts[$right] ?? 0) + $count;
} else {
if (function_exists('gmp_mul')) {
$result = gmp_strval(gmp_mul($stone, 2024));
$newStoneCounts[(int) $result] = ($newStoneCounts[(int) $result] ?? 0) + $count;
} else {
$newStoneCounts[$stone * 2024] = ($newStoneCounts[$stone * 2024] ?? 0) + $count;
}
}
}
$stoneCounts = $newStoneCounts;
}
return array_sum($stoneCounts);
}
private function hasEvenDigits(int $number): bool
{
return strlen((string) $number) % 2 === 0;
}
}
class AdventOfCodeController extends Controller
{
private string $dataPath;
...
private Day11 $dayEleven;
...
public function __construct()
{
$this->dataPath = base_path().'/path-to-data';
...
$this->dayEleven = new Day11($this->dataPath.'/input_11.txt');
...
}
...
$this->dayEleven->simulateBlinks(25),
$this->dayEleven->simulateBlinks(75),
...
}
Part One: 1456082
Part Two: 872382
See the Class
class Day12
{
/**
* @var array
*/
private array $data = [];
/**
* @param string $file
*/
public function __construct($file)
{
if (! file_exists($file)) {
throw new RuntimeException('File not found');
}
$input = file_get_contents($file);
if ($input === false) {
exit('Failed to read input file');
}
$this->processInput($input);
}
private function processInput(string $input): void
{
$lines = explode("\n", trim($input));
foreach ($lines as $i => $line) {
for ($j = 0; $j < strlen($line); $j++) {
$this->data["$i,$j"] = $line[$j];
}
}
}
/**
* @param array $p1
* @param array $p2
* @return array
*/
private function add(array $p1, array $p2): array
{
return [$p1[0] + $p2[0], $p1[1] + $p2[1]];
}
/**
* @param array>> $sets
*/
private function totalSides(array &$sets): int
{
$sides = [];
while (! empty($sets)) {
$pair = array_pop($sets);
[$loc, $out] = $pair;
$side = [$pair];
[$di, $dj] = $out;
$right = [$dj, -$di];
$left = [-$dj, $di];
$rloc = $this->add($loc, $right);
while (isset($sets[implode(',', $rloc).','.implode(',', $out)])) {
unset($sets[implode(',', $rloc).','.implode(',', $out)]);
$side[] = [$rloc, $out];
$rloc = $this->add($rloc, $right);
}
$lloc = $this->add($loc, $left);
while (isset($sets[implode(',', $lloc).','.implode(',', $out)])) {
unset($sets[implode(',', $lloc).','.implode(',', $out)]);
$side[] = [$lloc, $out];
$lloc = $this->add($lloc, $left);
}
$sides[] = $side;
}
return count($sides);
}
/**
* @return array
*/
public function calculateTotalPrice(): array
{
$dirs = [[0, 1], [1, 0], [0, -1], [-1, 0]];
$areas = [];
$a = [];
$b = [];
$part1 = 0;
$part2 = 0;
foreach ($this->data as $z => $c) {
if (isset($areas[$z])) {
continue;
}
$loc = array_map('intval', explode(',', (string) $z));
$new = [$z => true];
$b[] = &$new;
$a[$z] = &$new;
$s = [$loc];
$border = [];
while (! empty($s)) {
$nloc = array_pop($s);
foreach ($dirs as $d) {
$u = $this->add($nloc, $d);
$uStr = implode(',', $u);
if (isset($this->data[$uStr]) && $this->data[$uStr] === $c) {
if (! isset($areas[$uStr])) {
$s[] = $u;
$a[$uStr] = &$new;
$new[$uStr] = true;
$areas[$uStr] = true;
}
} else {
$borderKey = implode(',', $nloc).','.implode(',', $d);
$border[$borderKey] = [$nloc, $d];
}
}
}
$part1 += count($border) * count($new);
$part2 += $this->totalSides($border) * count($new);
}
return ['part1' => $part1, 'part2' => $part2];
}
}
class AdventOfCodeController extends Controller
{
private string $dataPath;
...
private Day12 $dayTwelve;
...
public function __construct()
{
$this->dataPath = base_path().'/path-to-data';
...
$this->dayTwelve = new Day12($this->dataPath.'/input_12.txt');
...
}
...
$this->day12->calculateTotalPrice(),
...
}
Part One: 27105
Part Two: 101726882250942
See the Class
class Day13
{
private string $input;
/**
* @var array, array>>
*/
private array $machines;
/**
* @param string $file
*/
public function __construct($file)
{
if (! file_exists($file)) {
throw new RuntimeException('File not found');
}
$input = file_get_contents($file);
if ($input === false) {
exit('Failed to read input file');
} else {
$this->input = $input;
}
$this->processInput();
}
private function processInput(): void
{
$machines = [];
$lines = explode("\n", trim($this->input));
for ($i = 0; $i < count($lines); $i += 4) {
if (! isset($lines[$i])) {
break;
}
if (! preg_match('/Button A: X\+(\d+), Y\+(\d+)/', $lines[$i], $matchesA)) {
continue;
}
$buttonA = [
'x' => (int) $matchesA[1],
'y' => (int) $matchesA[2],
];
if (! preg_match('/Button B: X\+(\d+), Y\+(\d+)/', $lines[$i + 1], $matchesB)) {
continue;
}
$buttonB = [
'x' => (int) $matchesB[1],
'y' => (int) $matchesB[2],
];
if (! preg_match('/Prize: X=(\d+), Y=(\d+)/', $lines[$i + 2], $matchesPrize)) {
continue;
}
$prize = [
'x' => (int) $matchesPrize[1],
'y' => (int) $matchesPrize[2],
];
$machines[] = [
'buttonA' => $buttonA,
'buttonB' => $buttonB,
'prize' => $prize,
];
}
$this->machines = $machines;
}
public function solvePart1(): int
{
$totalTokens = 0;
foreach ($this->machines as $machine) {
$result = $this->solveForMachine($machine);
if ($result !== null) {
$totalTokens += $result;
}
}
return $totalTokens;
}
/**
* @param array> $machine
*/
private function solveForMachine(array $machine): ?int
{
for ($a = 0; $a <= 100; $a++) {
for ($b = 0; $b <= 100; $b++) {
$x = $a * $machine['buttonA']['x'] + $b * $machine['buttonB']['x'];
$y = $a * $machine['buttonA']['y'] + $b * $machine['buttonB']['y'];
if ($x === $machine['prize']['x'] && $y === $machine['prize']['y']) {
return (3 * $a) + $b;
}
}
}
return null;
}
public function solvePart2(): string
{
$correction = '10000000000000';
$totalTokens = '0';
foreach ($this->machines as $machine) {
$result = $this->solveForMachinePart2($machine, $correction);
if ($result !== null) {
$totalTokens = bcadd($totalTokens, $result);
}
}
return $totalTokens;
}
/**
* @param array> $machine
*/
private function solveForMachinePart2(array $machine, string $correction): ?string
{
$a11 = (string) $machine['buttonA']['x'];
$a12 = (string) $machine['buttonB']['x'];
$a21 = (string) $machine['buttonA']['y'];
$a22 = (string) $machine['buttonB']['y'];
$b1 = bcadd((string) $machine['prize']['x'], $correction);
$b2 = bcadd((string) $machine['prize']['y'], $correction);
$detA = bcsub(bcmul($a11, $a22), bcmul($a12, $a21));
if ($detA === '0') {
return null;
}
$detX = bcsub(bcmul($b1, $a22), bcmul($a12, $b2));
$detY = bcsub(bcmul($a11, $b2), bcmul($b1, $a21));
$a = bcdiv($detX, $detA, 0);
$b = bcdiv($detY, $detA, 0);
$x = bcadd(bcmul($a, $a11), bcmul($b, $a12));
$y = bcadd(bcmul($a, $a21), bcmul($b, $a22));
$expectedX = bcadd((string) $machine['prize']['x'], $correction);
$expectedY = bcadd((string) $machine['prize']['y'], $correction);
if (bccomp($x, $expectedX) === 0 && bccomp($y, $expectedY) === 0) {
return bcadd(bcmul('3', $a), $b);
}
return null;
}
}
class AdventOfCodeController extends Controller
{
private string $dataPath;
...
private Day13 $dayThirteen;
...
public function __construct()
{
$this->dataPath = base_path().'/path-to-data';
...
$this->dayThirteen = new Day13($this->dataPath.'/input_13.txt');
...
}
...
$this->dayThirteen->solvePart1(),
$this->dayThirteen->solvePart2(),
...
}
Part One: 232253028
Part Two: 8079
See the Class
class Day14
{
private string $input;
/** @var array */
private array $robots;
private int $width;
private int $height;
/**
* @param string $file
*/
public function __construct($file)
{
if (! file_exists($file)) {
throw new RuntimeException('File not found');
}
$input = file_get_contents($file);
if ($input === false) {
exit('Failed to read input file');
} else {
$this->input = $input;
}
$this->processInput();
}
private function processInput(): void
{
$lines = explode("\n", trim($this->input));
$maxX = 0;
$maxY = 0;
foreach ($lines as $line) {
if (preg_match('/p=(\d+),(\d+) v=(-?\d+),(-?\d+)/', $line, $matches)) {
$x = (int) $matches[1];
$y = (int) $matches[2];
$maxX = max($maxX, $x);
$maxY = max($maxY, $y);
$this->robots[] = [
'position' => [$x, $y],
'velocity' => [(int) $matches[3], (int) $matches[4]],
];
}
}
$this->width = $maxX + 1;
$this->height = $maxY + 1;
}
public function simulate(int $seconds): int
{
for ($i = 0; $i < $seconds; $i++) {
foreach ($this->robots as &$robot) {
$newX = $robot['position'][0] + $robot['velocity'][0];
$newY = $robot['position'][1] + $robot['velocity'][1];
while ($newX < 0) {
$newX += $this->width;
}
while ($newX >= $this->width) {
$newX -= $this->width;
}
while ($newY < 0) {
$newY += $this->height;
}
while ($newY >= $this->height) {
$newY -= $this->height;
}
$robot['position'][0] = $newX;
$robot['position'][1] = $newY;
}
}
return $this->calculateSafetyFactor();
}
private function calculateSafetyFactor(): int
{
$quadrants = [0, 0, 0, 0];
$midX = floor($this->width / 2);
$midY = floor($this->height / 2);
foreach ($this->robots as $robot) {
[$x, $y] = $robot['position'];
if ($x < $midX && $y < $midY) {
$quadrants[0]++;
} elseif ($x > $midX && $y < $midY) {
$quadrants[1]++;
} elseif ($x < $midX && $y > $midY) {
$quadrants[2]++;
} elseif ($x > $midX && $y > $midY) {
$quadrants[3]++;
} else {
}
}
return (int) array_product($quadrants);
}
}
class AdventOfCodeController extends Controller
{
private string $dataPath;
...
private Day14 $dayFourteen;
...
public function __construct()
{
$this->dataPath = base_path().'/path-to-data';
...
$this->dayFourteen = new Day14($this->dataPath.'/input_14.txt');
...
}
...
$this->day14->simulate(100),
$this->day14->findChristmasTree()[0],
...
}
..........*....................................................*.....*...............................
..................................................................*..................................
.......................................*..............................................*..............
...*.........**......................................................................................
.....................................................................................................
....*.........*....................*............................................*....................
.........................................................................................*...........
..............................................................................................*......
.....................................................................................................
.....................................................................................................
....................................................*..............*.................................
.........*......................................................*.......*.......................*....
.....................................................................................................
.*......................*............................................................................
.......................................*.............................................................
.....................................................................................................
.................*..........................................................................*.......*
...............................................................**............................*.......
.....................................................................................................
.........*...........................................................................................
.....................................................................................................
.....................................................................................................
...................*.................................................................................
..................................**...................................*.............................
....................................................................................................*
..........................................................*..........................................
.....*............*..........................................................*.......................
......................*..............................................................................
.............................................................................*......*................
..*......................................*...........................................................
................................................................................................*....
...........................*..............................................................*..........
.............................................................................................*.......
.....................................................*******************************........*........
........................*............................*.............................*........*........
......................*..............................*.............................*.................
...............*................*....................*.............................*.................
............................*........................*.............................*.................
..*..................................................*..............*..............*.................
.....................................................*.............***.............*.................
..............................*......................*............*****............*.................
.....................................................*...........*******...........*.................
....................................*.*..............*..........*********..........*.................
..*..................................................*............*****............*.................
.....................................................*...........*******...........*.................
.....................................................*..........*********..........*.................
.....................................................*.........***********.........*......*.....*....
................................................*....*........*************........*..............*..
.....................................................*..........*********..........*.................
.....................................................*.........***********.........*.................
............................*.....................*..*........*************........*.................
.............................................*.......*.......***************.......*.................
.........................*...........................*......*****************......*.*...............
.....................................................*........*************........*.................
.............................................*.......*.......***************.......*.......*.........
.*................................*..................*......*****************......*.................
.................................*...........*.......*.....*******************.....*.................
.....................................................*....*********************....*.................
...................................................*.*.............***.............*.....*..........*
.................................................*...*.............***.............*.................
...*.................................................*.............***.............*.................
.............................................*.......*.............................*.................
..................................*..................*.............................*.................
............*........................................*.............................*.................
.....................................................*.............................*.................
.....................................................*******************************.................
..........*.............................*...................................................*........
.....................................................................................................
.....................................................................................................
...........*.........................................................................................
...............................................*...............................................*....*
.....................................................................................................
................................................................................................*....
.....................................................................................................
............................................................*........................................
.....................................................................................................
...............................................................*...*.................................
......................................................*..............................................
.....................................*.....................*.*.......................................
.......................................................................*.............................
........................................................*...................................*.....*..
..............*.....................................*..................*.............................
...............................*...........................................*.........................
..........................................................................................*.......*..
.................*...................................................................................
..........................*..........................................................................
................................*.....................................*.............*...........*....
...*........................................................................*..*.....................
..............................................................................*...................*..
.....*....*..........................................................................................
.....................................................................................................
................................................................................*..........*.*.....*.
.....................................................................................................
..............................................*......................................................
.....................*....................*..........................................................
...................................................................................................*.
.....................................................*...............................................
....................................................................*................................
...............*........*.........................*..................................................
...............................*................*......*.............................................
..............................*.*.............................................*......................
.................................................................*...*...............................
..................................*...............................................*.....*............
Part One: 1465152
Part Two: 1511259
See the Class
class Day15
{
/**
* @var array
*/
private array $data;
public function __construct(string $file)
{
if (! file_exists($file)) {
throw new RuntimeException('File not found');
}
$input = file_get_contents($file);
if ($input === false) {
exit('Failed to read input file');
}
$this->data = explode("\n\n", trim($input));
}
private function solve2(): void
{
$a = explode("\n", $this->data[0]);
$b = str_replace("\n", '', $this->data[1]);
$n = count($a);
$m = strlen($a[0]) * 2;
for ($i = 0; $i < $n; $i++) {
$na = [];
$chars = is_string($a[$i]) ? str_split($a[$i]) : $a[$i];
foreach ($chars as $c) {
switch ($c) {
case '#':
$na[] = '#';
$na[] = '#';
break;
case 'O':
$na[] = '[';
$na[] = ']';
break;
case '.':
$na[] = '.';
$na[] = '.';
break;
default:
$na[] = '@';
$na[] = '.';
}
}
$a[$i] = $na;
}
$sx = $sy = 0;
for ($i = 0; $i < $n; $i++) {
for ($j = 0; $j < $m; $j++) {
if ($a[$i][$j] === '@') {
$sx = $i;
$sy = $j;
$a[$i][$j] = '.';
}
}
}
$dx = [0, -1, 0, 1];
$dy = [-1, 0, 1, 0];
foreach (str_split($b) as $mv) {
$d = match ($mv) {
'^' => 1,
'<' => 0,
'>' => 2,
default => 3,
};
if ($this->trypush($a, $sx, $sy, $dx[$d], $dy[$d])) {
$this->push($a, $sx, $sy, $dx[$d], $dy[$d]);
$sx += $dx[$d];
$sy += $dy[$d];
}
}
$ans = 0;
for ($i = 0; $i < $n; $i++) {
for ($j = 0; $j < $m; $j++) {
if ($a[$i][$j] === '[') {
$ans += 100 * $i + $j;
}
}
}
echo $ans."\n";
}
/**
* @param array> $a
*/
private function trypush(array &$a, int $sx, int $sy, int $dx, int $dy): bool
{
$nx = $sx + $dx;
$ny = $sy + $dy;
if ($a[$nx][$ny] === '#') {
return false;
}
if ($a[$nx][$ny] === '.') {
return true;
}
if ($dy === 0) {
if ($a[$nx][$ny] === ']') {
return $this->trypush($a, $nx, $ny, $dx, $dy) &&
$this->trypush($a, $nx, $ny - 1, $dx, $dy);
}
if ($a[$nx][$ny] === '[') {
return $this->trypush($a, $nx, $ny, $dx, $dy) &&
$this->trypush($a, $nx, $ny + 1, $dx, $dy);
}
}
if ($dy === -1) { // push left
if ($a[$nx][$ny] === ']') {
return $this->trypush($a, $nx, $ny - 1, $dx, $dy);
}
}
if ($dy === 1) { // push right
if ($a[$nx][$ny] === '[') {
return $this->trypush($a, $nx, $ny + 1, $dx, $dy);
}
}
return false;
}
/**
* @param array> $a
*/
private function push(array &$a, int $sx, int $sy, int $dx, int $dy): void
{
$nx = $sx + $dx;
$ny = $sy + $dy;
if ($a[$nx][$ny] === '#') {
return;
}
if ($a[$nx][$ny] === '.') {
[$a[$sx][$sy], $a[$nx][$ny]] = [$a[$nx][$ny], $a[$sx][$sy]];
return;
}
if ($dy === 0) {
if ($a[$nx][$ny] === ']') {
$this->push($a, $nx, $ny, $dx, $dy);
$this->push($a, $nx, $ny - 1, $dx, $dy);
[$a[$sx][$sy], $a[$nx][$ny]] = [$a[$nx][$ny], $a[$sx][$sy]];
return;
}
if ($a[$nx][$ny] === '[') {
$this->push($a, $nx, $ny, $dx, $dy);
$this->push($a, $nx, $ny + 1, $dx, $dy);
[$a[$sx][$sy], $a[$nx][$ny]] = [$a[$nx][$ny], $a[$sx][$sy]];
return;
}
}
if ($dy === -1) { // push left
if ($a[$nx][$ny] === ']') {
$this->push($a, $nx, $ny - 1, $dx, $dy);
[$a[$nx][$ny - 1], $a[$nx][$ny], $a[$sx][$sy]] =
[$a[$nx][$ny], $a[$sx][$sy], $a[$nx][$ny - 1]];
return;
}
}
if ($dy === 1) { // push right
if ($a[$nx][$ny] === '[') {
$this->push($a, $nx, $ny + 1, $dx, $dy);
[$a[$nx][$ny + 1], $a[$nx][$ny], $a[$sx][$sy]] =
[$a[$nx][$ny], $a[$sx][$sy], $a[$nx][$ny + 1]];
return;
}
}
}
private function solve(): void
{
$a = explode("\n", $this->data[0]);
$b = str_replace("\n", '', $this->data[1]);
$ans = 0;
$n = count($a);
$m = strlen($a[0]);
$sx = $sy = 0;
for ($i = 0; $i < $n; $i++) {
$a[$i] = is_string($a[$i]) ? str_split($a[$i]) : $a[$i];
for ($j = 0; $j < $m; $j++) {
if ($a[$i][$j] === '@') {
$sx = $i;
$sy = $j;
$a[$i][$j] = '.';
break;
}
}
}
$dx = [0, -1, 0, 1];
$dy = [-1, 0, 1, 0];
foreach (str_split($b) as $mv) {
$d = match ($mv) {
'^' => 1,
'<' => 0,
'>' => 2,
default => 3,
};
$k = 1;
$flag = false;
$blocked = false;
while (true) {
$nx = $sx + $dx[$d] * $k;
$ny = $sy + $dy[$d] * $k;
if ($a[$nx][$ny] === '#') {
$blocked = true;
break;
} elseif ($a[$nx][$ny] === '.') {
$flag = true;
break;
}
$k++;
}
if (! $blocked) {
[$a[$nx][$ny], $a[$sx + $dx[$d]][$sy + $dy[$d]]] =
[$a[$sx + $dx[$d]][$sy + $dy[$d]], $a[$nx][$ny]];
$sx += $dx[$d];
$sy += $dy[$d];
}
}
for ($i = 0; $i < $n; $i++) {
for ($j = 0; $j < $m; $j++) {
if ($a[$i][$j] === 'O') {
$ans += 100 * $i + $j;
}
}
}
echo $ans."\n";
}
public function part1(): int
{
ob_start();
$this->solve();
$result = (int) ob_get_clean();
return $result;
}
public function part2(): int
{
ob_start();
$this->solve2();
$result = (int) ob_get_clean();
return $result;
}
}
class AdventOfCodeController extends Controller
{
private string $dataPath;
...
private Day15 $dayFifteen;
...
public function __construct()
{
$this->dataPath = base_path().'/path-to-data';
...
$this->dayFifteen = new Day15($this->dataPath.'/input_15.txt');
...
}
...
$this->dayFifteen->part1(),
$this->dayFifteen->part2(),
...
}
Part One: 82460
Part Two: 590
See the Class
class Day16
{
private string $input;
/**
* @var array>
*/
private array $grid = [];
/**
* @var array
*/
private array $start;
/**
* @var array
*/
private array $end;
private const DIRECTIONS = [
'N' => [-1, 0],
'E' => [0, 1],
'S' => [1, 0],
'W' => [0, -1],
];
public function __construct(string $file)
{
if (! file_exists($file)) {
throw new RuntimeException('File not found');
}
$input = file_get_contents($file);
if ($input === false) {
exit('Failed to read input file');
}
$this->input = $input;
$this->parseInput();
}
private function parseInput(): void
{
$lines = explode("\n", trim($this->input));
foreach ($lines as $i => $line) {
$this->grid[$i] = str_split($line);
foreach ($this->grid[$i] as $j => $char) {
if ($char === 'S') {
$this->start = [$i, $j];
} elseif ($char === 'E') {
$this->end = [$i, $j];
}
}
}
}
/**
* @return array{part1: int, part2: int}
*/
public function solve(): array
{
$all = [];
$z = [];
$startDir = [$this->start, self::DIRECTIONS['E']];
$all[json_encode($startDir)] = 0;
$z[json_encode($startDir)] = true;
while (! empty($z)) {
$key = array_key_first($z);
unset($z[$key]);
/** @var array{0: array{int, int}, 1: array{int, int}} $state */
$state = json_decode((string) $key, true);
$cost = $all[$key];
foreach ($this->next($state) as $new => $inc) {
/** @var array{0: array{int, int}, 1: array{int, int}} $newState */
$newState = json_decode((string) $new, true);
[$b, $c] = $newState;
if (! isset($this->grid[$b[0]][$b[1]]) || $this->grid[$b[0]][$b[1]] === '#') {
continue;
}
$newCost = $cost + $inc;
if (! isset($all[$new]) || $newCost < $all[$new]) {
$all[$new] = $newCost;
$z[$new] = true;
}
}
}
$min = PHP_INT_MAX;
$last = null;
foreach (self::DIRECTIONS as $dir) {
$state = json_encode([$this->end, $dir]);
if (isset($all[$state]) && $all[$state] < $min) {
$min = $all[$state];
$last = $state;
}
}
$xsOnPath = [$this->end[0].','.$this->end[1] => true];
$pending = [$last => true];
while (! empty($pending)) {
$key = array_key_first($pending);
unset($pending[$key]);
/** @var array{0: array{int, int}, 1: array{int, int}} $state */
$state = json_decode((string) $key, true);
$cost = $all[$key];
foreach ($this->prev($state) as $prevState => $inc) {
/** @var array{0: array{int, int}, 1: array{int, int}} $prevStateData */
$prevStateData = json_decode((string) $prevState, true);
[$d, $y] = $prevStateData;
if (! isset($this->grid[$d[0]][$d[1]]) || $this->grid[$d[0]][$d[1]] === '#') {
continue;
}
if (isset($all[$prevState]) && ($cost === $all[$prevState] + $inc)) {
$pending[$prevState] = true;
$xsOnPath[$d[0].','.$d[1]] = true;
}
}
}
return [
'part1' => $min,
'part2' => count($xsOnPath),
];
}
/**
* @param array{0: array{int, int}, 1: array{int, int}} $state
* @return array
*/
private function next(array $state): array
{
[$x, $dir] = $state;
$nextStates = [];
$b = $this->add($x, $dir);
$nextStates[json_encode([$b, $dir])] = 1;
$c = [$dir[1], -$dir[0]];
$nextStates[json_encode([$x, $c])] = 1000;
$c = [-$dir[1], $dir[0]];
$nextStates[json_encode([$x, $c])] = 1000;
return $nextStates;
}
/**
* @param array{0: array{int, int}, 1: array{int, int}} $state
* @return array
*/
private function prev(array $state): array
{
[$x, $dir] = $state;
$prev = [];
$d = $this->add($x, [-$dir[0], -$dir[1]]);
$prev[json_encode([$d, $dir])] = 1;
$y = [-$dir[1], $dir[0]];
$prev[json_encode([$x, $y])] = 1000;
$y = [$dir[1], -$dir[0]];
$prev[json_encode([$x, $y])] = 1000;
return $prev;
}
/**
* @param array{int, int} $p1
* @param array{int, int} $p2
* @return array{int|string, int}
*/
private function add(array $p1, array $p2): array
{
return [$p1[0] + $p2[0], $p1[1] + $p2[1]];
}
}
class AdventOfCodeController extends Controller
{
private string $dataPath;
...
private Day16 $daySixteen;
...
public function __construct()
{
$this->dataPath = base_path().'/path-to-data';
...
$this->daySixteen = new Day16($this->dataPath.'/input_16.txt');
...
}
...
$this->daySixteen->solve()['part1'],
$this->daySixteen->solve()['part2']
...
}
Part One: 6,5,4,7,1,6,0,3,1
Part Two: 106086382266778
See the Class
class Day17
{
private int $registerA;
private int $registerB;
private int $registerC;
/**
* @var array
*/
private array $program;
public function __construct(string $file)
{
if (! file_exists($file)) {
throw new RuntimeException('File not found');
}
$input = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
if ($input === false) {
exit('Failed to read input file');
}
$this->registerA = (int) $this->getData($input[0]);
$this->registerB = (int) $this->getData($input[1]);
$this->registerC = (int) $this->getData($input[2]);
$this->program = array_map('intval', explode(',', $this->getData($input[3])));
}
private function getData(string $line): string
{
return explode(': ', $line)[1];
}
private function combo(?int $a, ?int $b, ?int $c, ?int $value): ?int
{
return match ($value) {
0, 1, 2, 3 => $value,
4 => $a,
5 => $b,
6 => $c,
default => null,
};
}
/**
* @param array $program
* @return array
*/
private function eval(?int $a, ?int $b, ?int $c, ?int $ip, array $program): array
{
$opcode = $program[$ip];
$arg = $program[$ip + 1];
$comb = $this->combo($a, $b, $c, $arg);
switch ($opcode) {
case 0:
$num = $a;
$denom = 2 ** $comb;
return [null, (int) ($num / $denom), $b, $c, $ip + 2];
case 1:
return [null, $a, $b ^ $arg, $c, $ip + 2];
case 2:
return [null, $a, $comb % 8, $c, $ip + 2];
case 3:
return $a === 0 ? [null, $a, $b, $c, $ip + 2] : [null, $a, $b, $c, $arg];
case 4:
return [null, $a, $b ^ $c, $c, $ip + 2];
case 5:
return [$comb % 8, $a, $b, $c, $ip + 2];
case 6:
$num = $a;
$denom = 2 ** $comb;
return [null, $a, (int) ($num / $denom), $c, $ip + 2];
case 7:
$num = $a;
$denom = 2 ** $comb;
return [null, $a, $b, (int) ($num / $denom), $ip + 2];
default:
throw new RuntimeException('Invalid opcode: '.$opcode);
}
}
/**
* @param array $program
* @return array
*/
public function runProgram(int $a, int $b, int $c, array $program): array
{
$ip = 0;
$res = [];
while ($ip < count($program) - 1) {
[$out, $a, $b, $c, $ip] = $this->eval($a, $b, $c, $ip, $program);
if ($out !== null) {
$res[] = $out;
}
}
return $res;
}
/**
* @param array $program
*/
public function getBestQuineInput(array $program, int $cursor, int $sofar): ?int
{
for ($candidate = 0; $candidate < 8; $candidate++) {
if ($this->runProgram($sofar * 8 + $candidate, 0, 0, $program) === array_slice($program, $cursor)) {
if ($cursor === 0) {
return $sofar * 8 + $candidate;
}
$ret = $this->getBestQuineInput($program, $cursor - 1, $sofar * 8 + $candidate);
if ($ret !== null) {
return $ret;
}
}
}
return null;
}
/**
* @return array
*/
public function execute(): array
{
$output = $this->runProgram($this->registerA, $this->registerB, $this->registerC, $this->program);
return [
implode(',', $output),
$this->getBestQuineInput($this->program, count($this->program) - 1, 0),
];
}
}
class AdventOfCodeController extends Controller
{
private string $dataPath;
...
private Day17 $daySeventeen;
...
public function __construct()
{
$this->dataPath = base_path().'/path-to-data';
...
$this->daySeventeen = new Day17($this->dataPath.'/input_17.txt');
...
}
...
$this->daySeventeen->execute()[0],
$this->daySeventeen->execute()[1],
...
}
Part One: 371
Part Two: 2,5
See the Class
class Day18
{
/**
* @var array>
*/
private array $grid;
/**
* @var array>
*/
private array $bytes;
private int $gridSize;
private int $maxBytes;
public function __construct(string $file, int $gridSize, int $maxBytes)
{
if (! file_exists($file)) {
throw new RuntimeException('File not found');
}
$input = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
if ($input === false) {
exit('Failed to read input file');
}
$this->bytes = [];
$this->gridSize = $gridSize;
$this->maxBytes = $maxBytes;
$this->processInput($input);
$this->buildGrid();
}
/**
* @param array $input
*/
private function processInput(array $input): void
{
$bytes = [];
foreach ($input as $line) {
$bytes[] = array_map('intval', explode(',', $line));
}
$this->bytes = $bytes;
}
private function buildGrid(): void
{
for ($y = 0; $y < $this->gridSize; $y++) {
$this->grid[$y] = array_fill(0, $this->gridSize, '.');
}
$i = 0;
foreach ($this->bytes as $byte) {
if ($i > $this->maxBytes) {
break;
}
if (empty($byte)) {
continue;
}
$x = intval($byte[0]);
$y = intval($byte[1]);
if ($x >= 0 && $x < $this->gridSize &&
$y >= 0 && $y < $this->gridSize) {
$this->grid[$x][$y] = '#';
}
$i++;
}
}
public function findShortestPath(): int
{
$queue = new SplPriorityQueue;
$visited = array_fill(0, $this->gridSize, array_fill(0, $this->gridSize, false));
$distances = array_fill(0, $this->gridSize, array_fill(0, $this->gridSize, PHP_INT_MAX));
$directions = [[0, 1], [1, 0], [0, -1], [-1, 0]];
$distances[0][0] = 0;
$queue->insert([0, 0], 0);
$queue->setExtractFlags(SplPriorityQueue::EXTR_BOTH);
while (! $queue->isEmpty()) {
/**
* @var array{data: array{0: int, 1: int}}
*/
$current = $queue->extract();
$data = (array) $current['data'];
[$x, $y] = $data;
if ($visited[$y][$x]) {
continue;
}
$visited[$y][$x] = true;
$currentDist = $distances[$y][$x];
if ($x === $this->gridSize - 1 && $y === $this->gridSize - 1) {
return (int) $currentDist;
}
foreach ($directions as [$dx, $dy]) {
$newX = $x + $dx;
$newY = $y + $dy;
if ($newX >= 0 && $newX < $this->gridSize &&
$newY >= 0 && $newY < $this->gridSize &&
! $visited[$newY][$newX] &&
$this->grid[$newY][$newX] === '.') {
$newDist = $currentDist + 1;
if ($newDist < $distances[$newY][$newX]) {
$distances[$newY][$newX] = $newDist;
$queue->insert([$newX, $newY], -$newDist);
}
}
}
}
return -1;
}
public function findBlockingByte(): string
{
for ($i = 0; $i < count($this->bytes); $i++) {
$byte = $this->bytes[$i];
$x = intval($byte[0]);
$y = intval($byte[1]);
if ($x >= 0 && $x < $this->gridSize &&
$y >= 0 && $y < $this->gridSize) {
$this->grid[$x][$y] = '#';
}
if (! $this->isPathPossible()) {
return "$x,$y";
}
}
return '';
}
private function isPathPossible(): bool
{
$queue = new SplPriorityQueue;
$visited = array_fill(0, $this->gridSize, array_fill(0, $this->gridSize, false));
$directions = [[0, 1], [1, 0], [0, -1], [-1, 0]];
$queue->insert([0, 0], 0);
while (! $queue->isEmpty()) {
$current = (array) $queue->extract();
[$x, $y] = $current;
if ($x === $this->gridSize - 1 && $y === $this->gridSize - 1) {
return true;
}
foreach ($directions as [$dx, $dy]) {
$newX = $x + $dx;
$newY = $y + $dy;
if ($newX >= 0 && $newX < $this->gridSize &&
$newY >= 0 && $newY < $this->gridSize &&
! $visited[$newY][$newX] &&
$this->grid[$newY][$newX] === '.') {
$visited[$newY][$newX] = true;
$queue->insert([$newX, $newY], 0);
}
}
}
return false;
}
}
class AdventOfCodeController extends Controller
{
private string $dataPath;
...
private Day18 $dayEighteen;
...
public function __construct()
{
$this->dataPath = base_path().'/path-to-data';
...
$this->dayEighteen = new Day18($this->dataPath.'/input_18.txt', 71, 1024);
...
}
...
$this->dayEighteen->findShortestPath(),
$this->dayEighteen->findBlockingByte(),
...
}
Part One: 371
Part Two: 650354687260341
See the Class
class Day19
{
/**
* @var array
*/
private array $patterns;
/**
* @var array
*/
private array $designs;
/**
* @var array>
*/
private array $memo = [];
/**
* @param string $file
*/
public function __construct($file)
{
if (! file_exists($file)) {
throw new RuntimeException('File not found');
}
$input = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
if ($input === false) {
exit('Failed to read input file');
}
$this->processInput($input);
}
/**
* @param array $input
*/
private function processInput(array $input): void
{
$this->patterns = array_map('trim', explode(',', $input[0]));
array_shift($input);
$this->designs = $input;
}
public function solvePart1(): int
{
$possibleDesigns = 0;
foreach ($this->designs as $design) {
if ($this->canCreateDesign($design)) {
$possibleDesigns++;
}
}
return $possibleDesigns;
}
public function solvePart2(): int
{
$this->memo = [];
$totalCombinations = 0;
foreach ($this->designs as $design) {
$combinations = $this->countCombinations($design, 0);
$totalCombinations += $combinations;
}
return $totalCombinations;
}
private function canCreateDesign(string $design): bool
{
return $this->findCombination($design, 0);
}
private function findCombination(string $remaining, int $startPos): bool
{
if ($startPos >= strlen($remaining)) {
return true;
}
foreach ($this->patterns as $pattern) {
if (strlen($remaining) - $startPos >= strlen($pattern) &&
substr($remaining, $startPos, strlen($pattern)) === $pattern) {
if ($this->findCombination($remaining, $startPos + strlen($pattern))) {
return true;
}
}
}
return false;
}
private function countCombinations(string $remaining, int $startPos): int
{
$key = $startPos;
if (isset($this->memo[$remaining][$key])) {
return $this->memo[$remaining][$key];
}
if ($startPos >= strlen($remaining)) {
return 1;
}
$combinations = 0;
foreach ($this->patterns as $pattern) {
if (strlen($remaining) - $startPos >= strlen($pattern) &&
substr($remaining, $startPos, strlen($pattern)) === $pattern) {
$combinations += $this->countCombinations($remaining, $startPos + strlen($pattern));
}
}
$this->memo[$remaining][$key] = $combinations;
return $combinations;
}
}
class AdventOfCodeController extends Controller
{
private string $dataPath;
...
private Day19 $dayNineteen;
...
public function __construct()
{
$this->dataPath = base_path().'/path-to-data';
...
$this->dayNineteen = new Day19($this->dataPath.'/input_19.txt');
...
}
...
$this->dayNineteen->solvePart1(),
$this->dayNineteen->solvePart2(),
...
}
Part One: 1402
Part Two: 1020244
See the Class
class Day20
{
/**
* @var array
*/
private array $map;
/**
* @var array
*/
private array $start;
/**
* @var array
*/
private array $end;
private int $width;
private int $height;
/**
* @param string $file
*/
public function __construct($file)
{
if (! file_exists($file)) {
throw new RuntimeException('File not found');
}
$input = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
if ($input === false) {
exit('Failed to read input file');
}
$this->map = $input;
$this->width = strlen($input[0]);
$this->height = count($input);
$this->start = $this->findPosition('S');
$this->end = $this->findPosition('E');
}
/**
* @return array
*/
private function findPosition(string $char): array
{
for ($y = 0; $y < $this->height; $y++) {
for ($x = 0; $x < $this->width; $x++) {
if ($this->map[$y][$x] === $char) {
return [$y, $x];
}
}
}
throw new RuntimeException("Position $char not found");
}
/**
* @param array $start
* @return array
*/
private function bfs(array $start): array
{
$unreachable = 10 ** 10;
$states = [];
$states["{$start[0]},{$start[1]}"] = 0;
$toUpdate = [$start];
while (! empty($toUpdate)) {
$state = array_pop($toUpdate);
$cost = $states["{$state[0]},{$state[1]}"];
foreach ([[-1, 0], [1, 0], [0, -1], [0, 1]] as [$dy, $dx]) {
$newY = $state[0] + $dy;
$newX = $state[1] + $dx;
if ($newY < 0 || $newY >= $this->height ||
$newX < 0 || $newX >= $this->width ||
$this->map[$newY][$newX] === '#') {
continue;
}
$newKey = "$newY,$newX";
$newCost = $cost + 1;
if (! isset($states[$newKey]) || $newCost < $states[$newKey]) {
$states[$newKey] = $newCost;
$toUpdate[] = [$newY, $newX];
}
}
}
return $states;
}
/**
* @param array $loc
* @return array>
*/
private function generateManhattanPoints(array $loc, int $dist): array
{
$points = [];
[$i, $j] = $loc;
for ($di = 0; $di < $dist; $di++) {
$dj = $dist - $di;
$points[] = [$i + $di, $j + $dj];
$points[] = [$i + $dj, $j - $di];
$points[] = [$i - $di, $j - $dj];
$points[] = [$i - $dj, $j + $di];
}
return $points;
}
/**
* @return array
*/
public function findCheats(int $savedTime): array
{
$endStates = $this->bfs($this->end);
$startStates = $this->bfs($this->start);
$fullCost = $startStates["{$this->end[0]},{$this->end[1]}"];
$cheatsP1 = 0;
$cheatsP2 = 0;
for ($y = 0; $y < $this->height; $y++) {
for ($x = 0; $x < $this->width; $x++) {
if ($this->map[$y][$x] !== '#') {
continue;
}
foreach ([[-1, 0], [1, 0], [0, -1], [0, 1]] as [$dy, $dx]) {
$beforeY = $y + $dy; // Changed from y - dy
$beforeX = $x + $dx; // Changed from x - dx
$afterY = $y - $dy; // Changed from y + dy
$afterX = $x - $dx; // Changed from x + dx
$beforeKey = "$beforeY,$beforeX";
$afterKey = "$afterY,$afterX";
if (isset($startStates[$beforeKey]) && isset($endStates[$afterKey])) {
$savings = $fullCost - $endStates[$afterKey] - $startStates[$beforeKey] - 2;
if ($savings >= $savedTime) {
$cheatsP1++;
}
}
}
}
}
for ($y = 0; $y < $this->height; $y++) {
for ($x = 0; $x < $this->width; $x++) {
if ($this->map[$y][$x] === '#') {
continue;
}
$startKey = "$y,$x";
if (! isset($startStates[$startKey])) {
continue;
}
for ($dist = 1; $dist <= 20; $dist++) {
foreach ($this->generateManhattanPoints([$y, $x], $dist) as $endPoint) {
[$ey, $ex] = $endPoint;
if ($ey < 0 || $ey >= $this->height ||
$ex < 0 || $ex >= $this->width ||
$this->map[$ey][$ex] === '#') {
continue;
}
$endKey = "$ey,$ex";
if (! isset($endStates[$endKey])) {
continue;
}
$savings = $fullCost - $endStates[$endKey] - $startStates[$startKey] - $dist;
if ($savings >= $savedTime) {
$cheatsP2++;
}
}
}
}
}
return [$cheatsP1, $cheatsP2];
}
}
class AdventOfCodeController extends Controller
{
private string $dataPath;
...
private Day20 $dayTwenty;
...
public function __construct()
{
$this->dataPath = base_path().'/path-to-data';
...
$this->dayTwenty = new Day20($this->dataPath.'/input_20.txt');
...
}
...
$this->dayTwenty->findCheats(100)[0],
$this->dayTwenty->findCheats(100)[1],
...
}
Part One: 171596
Part Two: 209268004868246
See the Class
class Day21
{
private string $input;
/** @var array|string> */
private array $numPad;
/**
* @var array|string>
*/
private array $dirPad;
/**
* @param string $file
*/
public function __construct($file)
{
if (! file_exists($file)) {
throw new RuntimeException('File not found');
}
$input = file_get_contents($file);
if ($input === false) {
exit('Failed to read input file');
} else {
$this->input = $input;
}
$this->initializePads();
$this->input = trim($this->input);
}
private function initializePads(): void
{
$numPadLines = trim('789
456
123
.0A
');
$dirPadLines = trim('
.^A
');
$this->numPad = [];
$this->dirPad = [];
foreach (explode("\n", $numPadLines) as $i => $line) {
foreach (str_split($line) as $j => $c) {
if ($c !== '.') {
$this->numPad["$i,$j"] = $c;
$this->numPad[$c] = [$i, $j];
}
}
}
foreach (explode("\n", $dirPadLines) as $i => $line) {
foreach (str_split($line) as $j => $c) {
if ($c !== '.') {
$this->dirPad["$i,$j"] = $c;
$this->dirPad[$c] = [$i, $j];
}
}
}
}
/**
* @param array|string> $pad
*/
private function step(string $source, string $target, array $pad): string
{
$targetPos = $pad[$target];
$sourcePos = $pad[$source];
if (! is_array($targetPos) || ! is_array($sourcePos)) {
return '';
}
[$ti, $tj] = $targetPos;
[$si, $sj] = $sourcePos;
$di = (int) $ti - (int) $si;
$dj = (int) $tj - (int) $sj;
$vert = str_repeat('v', max(0, $di)).str_repeat('^', max(0, -$di));
$horiz = str_repeat('>', max(0, $dj)).str_repeat('<', max(0, -$dj));
if ($dj > 0 && isset($pad["$ti,$sj"])) {
return $vert.$horiz.'A';
}
if (isset($pad["$si,$tj"])) {
return $horiz.$vert.'A';
}
if (isset($pad["$ti,$sj"])) {
return $vert.$horiz.'A';
}
return '';
}
/**
* @param array|string> $pad
*/
private function routes(string $path, array $pad): string
{
$out = [];
$start = 'A';
foreach (str_split($path) as $end) {
$out[] = $this->step($start, $end, $pad);
$start = $end;
}
return implode('', $out);
}
public function part1(): int
{
$lines = explode("\n", trim($this->input));
$numRoutes = array_map(fn ($line) => $this->routes($line, $this->numPad), $lines);
$radRoutes = array_map(fn ($route) => $this->routes($route, $this->dirPad), $numRoutes);
$coldRoutes = array_map(fn ($route) => $this->routes($route, $this->dirPad), $radRoutes);
return array_sum(array_map(
fn ($route, $line) => strlen($route) * intval(substr($line, 0, -1)),
$coldRoutes,
$lines
));
}
public function part2(): int
{
$lines = explode("\n", trim($this->input));
$numRoutes = array_map(fn ($line) => $this->routes($line, $this->numPad), $lines);
$robotRoutes = array_map(fn ($route) => [$route => 1], $numRoutes);
for ($i = 0; $i < 25; $i++) {
$newRoutes = [];
foreach ($robotRoutes as $routeCounter) {
$newRoute = [];
foreach ($routeCounter as $subRoute => $qty) {
$newCounts = $this->routes2($subRoute, $this->dirPad);
foreach ($newCounts as $k => $v) {
$newCounts[$k] *= $qty;
}
foreach ($newCounts as $k => $v) {
$newRoute[$k] = ($newRoute[$k] ?? 0) + $v;
}
}
$newRoutes[] = $newRoute;
}
$robotRoutes = $newRoutes;
}
return array_sum(array_map(
fn ($route, $line) => $this->routeLen($route) * intval(substr($line, 0, -1)),
$robotRoutes,
$lines
));
}
/**
* @param array|string> $pad
* @return array
*/
private function routes2(string $path, array $pad): array
{
$out = [];
$start = 'A';
foreach (str_split($path) as $end) {
$step = $this->step($start, $end, $pad);
$out[$step] = ($out[$step] ?? 0) + 1;
$start = $end;
}
return $out;
}
/**
* @param array $route
*/
private function routeLen(array $route): int
{
return array_sum(array_map(
fn ($k, $v) => strlen($k) * $v,
array_keys($route),
array_values($route)
));
}
}
class AdventOfCodeController extends Controller
{
private string $dataPath;
...
private Day21 $dayTwentyOne;
...
public function __construct()
{
$this->dataPath = base_path().'/path-to-data';
...
$this->dayTwentyOne = new Day21($this->dataPath.'/input_21.txt');
...
}
...
$this->dayTwentyOne->part1(),
$this->dayTwentyOne->part2(),
...
}
Part One: 14082561342
Part Two: 1568
See the Class
class Day22
{
private string $input;
/**
* @var array
*/
private array $nums = [];
private const PRUNE_MOD = 16777216;
/**
* @param string $file
*/
public function __construct($file)
{
if (! file_exists($file)) {
throw new RuntimeException('File not found');
}
$input = file_get_contents($file);
if ($input === false) {
exit('Failed to read input file');
} else {
$this->input = $input;
}
$this->processInput();
}
private function processInput(): void
{
$this->nums = array_map('intval', explode("\n", trim($this->input)));
}
public function part1(): int
{
$p1 = 0;
$costs = [];
foreach ($this->nums as $secret) {
$costs[] = [];
for ($i = 0; $i < 2000; $i++) {
$secret = ($secret << 6 ^ $secret) % self::PRUNE_MOD;
$secret = ($secret >> 5 ^ $secret) % self::PRUNE_MOD;
$secret = ($secret << 11 ^ $secret) % self::PRUNE_MOD;
$costs[count($costs) - 1][] = $secret % 10;
}
$p1 += $secret;
}
return $p1;
}
public function part2(): int
{
$costs = [];
foreach ($this->nums as $secret) {
$costs[] = [];
for ($i = 0; $i < 2000; $i++) {
$secret = ($secret << 6 ^ $secret) % self::PRUNE_MOD;
$secret = ($secret >> 5 ^ $secret) % self::PRUNE_MOD;
$secret = ($secret << 11 ^ $secret) % self::PRUNE_MOD;
$costs[count($costs) - 1][] = $secret % 10;
}
}
$costDeltas = [];
foreach ($costs as $cost) {
$deltas = [];
for ($i = 0; $i < count($cost) - 1; $i++) {
$deltas[] = $cost[$i + 1] - $cost[$i];
}
$costDeltas[] = $deltas;
}
$totalCounts = [];
foreach ($costDeltas as $i => $deltas) {
$thisCount = [];
for ($j = 0; $j < count($deltas) - 4; $j++) {
$pattern = implode(',', array_slice($deltas, $j, 4));
if (! isset($thisCount[$pattern])) {
$thisCount[$pattern] = $costs[$i][$j + 4];
}
}
foreach ($thisCount as $pattern => $count) {
if (! isset($totalCounts[$pattern])) {
$totalCounts[$pattern] = 0;
}
$totalCounts[$pattern] += $count;
}
}
return max($totalCounts);
}
}
class AdventOfCodeController extends Controller
{
private string $dataPath;
...
private Day22 $dayTwentyTwo;
...
public function __construct()
{
$this->dataPath = base_path().'/path-to-data';
...
$this->dayTwentyTwo = new Day22($this->dataPath.'/input_22.txt');
...
}
...
$this->dayTwentyTwo->part1(),
$this->dayTwentyTwo->part2(),
...
}
Part One: 1200
Part Two: ag,gh,hh,iv,jx,nq,oc,qm,rb,sm,vm,wu,zr
See the Class
class Day23
{
/**
* @var array>
*/
private array $connected = [];
/**
* @param string $file
*/
public function __construct($file)
{
if (! file_exists($file)) {
throw new RuntimeException('File not found');
}
$input = file_get_contents($file);
if ($input === false) {
exit('Failed to read input file');
}
$this->processInput(trim($input));
}
private function processInput(string $input): void
{
$lines = explode("\n", $input);
$pairs = array_map(fn ($line) => explode('-', $line), $lines);
foreach ($pairs as [$a, $b]) {
$this->connected[$a][] = $b;
$this->connected[$b][] = $a;
}
foreach ($this->connected as &$connections) {
$connections = array_unique($connections);
}
}
public function part1(): int
{
$sets = [];
foreach ($this->connected as $first => $adjs) {
foreach ($adjs as $second) {
$commonNodes = array_intersect(
$this->connected[$second] ?? [],
$adjs
);
foreach ($commonNodes as $third) {
if ($third !== $first && $third !== $second) {
$triple = [$first, $second, $third];
sort($triple);
$sets[] = $triple;
}
}
}
}
$sets = array_map(
fn (array $triple) => json_encode($triple, JSON_THROW_ON_ERROR),
$sets
);
$sets = array_unique($sets);
$sets = array_map(
fn ($json) => json_decode($json, true, 512, JSON_THROW_ON_ERROR),
$sets
);
$keep = array_filter($sets, function (mixed $set): bool {
if (! is_array($set)) {
return false;
}
return array_reduce($set, function (bool $carry, string $node): bool {
return $carry || str_starts_with($node, 't');
}, false);
});
return count($keep);
}
public function part2(): string
{
return $this->password([], array_keys($this->connected), []);
}
/**
* Undocumented function
*
* @param array $r
* @param array $p
* @param array $x
*/
private function password(array $r, array $p, array $x): string
{
if (empty($p) && empty($x)) {
return implode(',', $r);
}
$max = '';
$pCopy = $p;
// Sort the candidates to ensure we process them in lexicographical order
sort($pCopy);
foreach ($pCopy as $v) {
$newR = array_merge($r, [$v]);
$newP = array_intersect($p, $this->connected[$v] ?? []);
$newX = array_intersect($x, $this->connected[$v] ?? []);
$pw = $this->password($newR, $newP, $newX);
if (strlen($pw) > strlen($max) || (strlen($pw) === strlen($max) && $pw < $max)) {
$max = $pw;
}
$p = array_diff($p, [$v]);
$x = array_merge($x, [$v]);
}
return $max;
}
}
class AdventOfCodeController extends Controller
{
private string $dataPath;
...
private Day23 $dayTwentyThree;
...
public function __construct()
{
$this->dataPath = base_path().'/path-to-data';
...
$this->dayTwentyThree = new Day23($this->dataPath.'/input_23.txt');
...
}
...
$this->dayTwentyThree->part1(),
$this->dayTwentyThree->part2(),
...
}
Part One: 60714423975686
Part Two: cgh,frt,pmd,sps,tst,z05,z11,z23
See the Class
class Day24
{
private string $input;
/**
* @var array
*/
private array $wires = [];
/**
* @var array>
*/
private array $rules = [];
/**
* @var array
*/
private array $ops;
/**
* @param string $file
*/
public function __construct($file)
{
if (! file_exists($file)) {
throw new RuntimeException('File not found');
}
$input = file_get_contents($file);
if ($input === false) {
exit('Failed to read input file');
} else {
$this->input = $input;
}
$this->ops = [
'AND' => fn ($a, $b) => $a & $b,
'OR' => fn ($a, $b) => $a | $b,
'XOR' => fn ($a, $b) => $a ^ $b,
];
$this->processInput();
}
private function processInput(): void
{
$lines = explode("\n", trim($this->input));
$midIndex = array_search('', $lines);
for ($i = 0; $i < $midIndex; $i++) {
[$key, $value] = explode(': ', $lines[$i]);
$this->wires[$key] = (int) $value;
}
$ruleLines = array_slice($lines, $midIndex + 1);
foreach ($ruleLines as $line) {
$parts = explode(' ', $line);
if (count($parts) >= 5) {
$this->rules[$parts[4]] = array_slice($parts, 0, 3);
}
}
}
public function part1(): int
{
$ruleQueue = array_keys($this->rules);
while (! empty($ruleQueue)) {
$wire = array_pop($ruleQueue);
if (isset($this->wires[$wire])) {
continue;
}
[$left, $op, $right] = $this->rules[$wire];
$leftOp = $this->wires[$left] ?? null;
$rightOp = $this->wires[$right] ?? null;
if ($leftOp === null || $rightOp === null) {
$ruleQueue[] = $wire;
if ($leftOp === null) {
$ruleQueue[] = $left;
}
if ($rightOp === null) {
$ruleQueue[] = $right;
}
} else {
$this->wires[$wire] = ($this->ops[$op])($leftOp, $rightOp);
}
}
$zWires = array_filter(array_keys($this->wires), fn ($wire) => str_starts_with($wire, 'z'));
rsort($zWires);
$zVals = implode('', array_map(fn ($wire) => $this->wires[$wire], $zWires));
return (int) bindec($zVals);
}
public function part2(): string
{
$wrong = [];
$highestZ = 'z00';
foreach ($this->rules as $wire => $rule) {
if (str_starts_with($wire, 'z') && $wire > $highestZ) {
$highestZ = $wire;
}
}
foreach ($this->rules as $wire => $rule) {
[$op1, $op, $op2] = $rule;
if (str_starts_with($wire, 'z') && $op !== 'XOR' && $wire !== $highestZ) {
$wrong[] = $wire;
}
if ($op === 'XOR'
&& ! str_starts_with($wire, 'x') && ! str_starts_with($wire, 'y') && ! str_starts_with($wire, 'z')
&& ! str_starts_with($op1, 'x') && ! str_starts_with($op1, 'y') && ! str_starts_with($op1, 'z')
&& ! str_starts_with($op2, 'x') && ! str_starts_with($op2, 'y') && ! str_starts_with($op2, 'z')
) {
$wrong[] = $wire;
}
if ($op === 'AND' && $op1 !== 'x00' && $op2 !== 'x00') {
foreach ($this->rules as $subRule) {
if (($wire === $subRule[0] || $wire === $subRule[2]) && $subRule[1] !== 'OR') {
$wrong[] = $wire;
break;
}
}
}
if ($op === 'XOR') {
foreach ($this->rules as $subRule) {
if (($wire === $subRule[0] || $wire === $subRule[2]) && $subRule[1] === 'OR') {
$wrong[] = $wire;
break;
}
}
}
}
$wrong = array_unique($wrong);
sort($wrong);
return implode(',', $wrong);
}
}
class AdventOfCodeController extends Controller
{
private string $dataPath;
...
private Day24 $dayTwentyFour;
...
public function __construct()
{
$this->dataPath = base_path().'/path-to-data';
...
$this->dayTwentyFour = new Day24($this->dataPath.'/input_24.txt');
...
}
...
$this->dayTwentyFour->part1(),
$this->dayTwentyFour->part2(),
...
}
Part One: 3146
Part Two: Merry Christmas!
See the Class
class Day25
{
/**
* @var array, array>
*/
private array $keys = [];
/**
* @var array, array>
*/
private array $locks = [];
/**
* @param string $file
*/
public function __construct($file)
{
if (! file_exists($file)) {
throw new RuntimeException('File not found');
}
$input = file_get_contents($file);
if ($input === false) {
exit('Failed to read input file');
}
$this->processInput($input);
}
private function processInput(string $input): void
{
$schematics = explode("\n\n", $input);
$this->processSchematics($schematics);
}
/**
* @param array $schematics
*/
private function processSchematics($schematics): void
{
$locks = [];
$keys = [];
foreach ($schematics as $schematic) {
$lines = explode("\n", $schematic);
if ($lines[0] == '#####') {
$locks[] = $lines;
} else {
$keys[] = $lines;
}
}
$this->processLocks($locks);
$this->processKeys($keys);
}
/**
* @param array> $locks
*/
private function processLocks($locks): void
{
$processedLocks = [];
$ret = [];
$i = 0;
foreach ($locks as $lock) {
foreach ($lock as $line) {
$processedLocks[$i][] = str_split($line);
}
$i++;
}
$h = count($processedLocks[0]);
$d = count($processedLocks[0][0]);
foreach ($processedLocks as $k => $lock) {
for ($i = 0; $i < $d; $i++) {
$x = 0;
for ($j = 0; $j < $h; $j++) {
if ($lock[$j][$i] == '#') {
$x++;
}
}
$ret[$k][] = $x - 1;
}
}
$this->locks = $ret;
}
/**
* @param array> $keys
*/
private function processKeys($keys): void
{
$processedKeys = [];
$ret = [];
$i = 0;
foreach ($keys as $key) {
foreach ($key as $line) {
$processedKeys[$i][] = str_split($line);
}
$i++;
}
$h = count($processedKeys[0]);
$d = count($processedKeys[0][0]);
foreach ($processedKeys as $k => $key) {
for ($i = 0; $i < $d; $i++) {
$x = 0;
for ($j = 0; $j < $h; $j++) {
if ($key[$j][$i] == '#') {
$x++;
}
}
$ret[$k][] = $x - 1;
}
}
$this->keys = $ret;
}
private function checkKey(int $key, int $lock): bool
{
return $key + $lock <= 5;
}
public function part1(): int
{
$total = 0;
$pass = true;
$pos = count($this->keys[0]);
foreach ($this->keys as $key) {
foreach ($this->locks as $lock) {
for ($i = 0; $i < $pos; $i++) {
if ($this->checkKey($key[$i], $lock[$i])) {
$pass = true;
} else {
$pass = false;
break;
}
}
if ($pass) {
$total++;
}
}
}
return $total;
}
}
class AdventOfCodeController extends Controller
{
private string $dataPath;
...
private Day25 $dayTwentyFive;
...
public function __construct()
{
$this->dataPath = base_path().'/path-to-data';
...
$this->dayTwentyFive = new Day25($this->dataPath.'/input_25.txt');
...
}
...
$this->dayTwentyFive->part1(),
...
}