explode(' ', $line), $lines); } class Report { public function __construct( public array $rawLevels, public array $safeLevels = [], private bool $allIncreasing = true, private bool $allDecreasing = true, private bool $safeDifference = true, ) { $this->rawLevels = array_map(fn ($item) => (int)$item, $this->rawLevels); $this->calculateLevelSafety(); } public function isSafe(): bool { return $this->safeDifference && ($this->allIncreasing xor $this->allDecreasing); } public function isSafeWithDampener(): bool { if ($this->isSafe()) { return true; } $unsafe = $this->getUnsafeKeys(); $unsafeCount = count($unsafe); foreach ($unsafe as $key) { $front = array_slice($this->rawLevels, 0, $key); $back = array_slice($this->rawLevels, $key + 1); $newRawLevels = array_merge($front, $back); $newReport = new Report($newRawLevels); $isNowSafe = $newReport->isSafe(); if ($isNowSafe) { // echo json_encode([ // 'old' => $this, // 'new' => $newReport // ], JSON_PRETTY_PRINT); return true; } } return false; } private function getUnsafeKeys(): array { $out = []; foreach ($this->safeLevels as $key => $value) { if (!$value) { $out[] = $key; } } return $out; } private function calculateLevelSafety (): void { $allIncreasing = true; $allDecreasing = true; $safeDifference = true; $safeLevels = []; $safeDiff = function ($curr, $prev): bool { $diff = abs($curr - $prev); return $diff > 0 && $diff < 4; }; $increasing = fn ($curr, $prev): bool => $curr > $prev; $decreasing = fn ($curr, $prev): bool => $curr < $prev; for ($i = 1; $i < count($this->rawLevels); $i++) { $curr = $this->rawLevels[$i]; $prev = $this->rawLevels[$i - 1]; $isIncreasing = $increasing($curr, $prev); $isDecreasing = $decreasing($curr, $prev); $isSafeDiff = $safeDiff($curr, $prev); $this->allIncreasing = $this->allIncreasing && $isIncreasing; $this->allDecreasing = $this->allDecreasing && $isDecreasing; $this->safeDifference = $this->safeDifference && $isSafeDiff; $safeLevel = $isSafeDiff && ( $this->allIncreasing xor $this->allDecreasing ); $this->safeLevels[] = $safeLevel; } } } // ---------------------------------------------------------------------------- $testReports = array_map(fn (array $line) => new Report($line), parse_reports('./test_input.txt')); $testSafeReports = array_filter($testReports, fn (Report $r) => $r->isSafe()); $testSafeReportsMap = array_map(fn (Report $r) => $r->isSafe(), $testReports); assert($testSafeReportsMap == [true, false, false, false, false, true]); assert(count($testSafeReports)==2, 'First example has 2 safe reports'); $testSafeReportsPart2 = array_filter($testReports, fn (Report $r) => $r->isSafeWithDampener()); $testSafeReportsPart2Map = array_map(fn (Report $r) => $r->isSafeWithDampener(), $testReports); assert($testSafeReportsPart2Map == [true, false, false, true, true, true]); assert(count($testSafeReportsPart2)==4); // ---------------------------------------------------------------------------- $reports = array_map(fn (array $line) => new Report($line), parse_reports()); $safeReports = array_filter($reports, fn (Report $r) => $r->isSafe()); $simpleSafeReports = count($safeReports); $allSafeReports = array_filter($reports, fn (Report $r) => $r->isSafeWithDampener()); $dampenedSafeReports = count($allSafeReports); $maybeValid = $dampenedSafeReports > 550 && $dampenedSafeReports != 557 && $dampenedSafeReports < 572; echo "(Part 1) Number of safe reports: $simpleSafeReports\n"; echo "(Part 2) Number of safe reports with dampener: $dampenedSafeReports\n"; echo "Part two may be valid: " . $maybeValid . "\n";