diff --git a/2024/day2/README.md b/2024/day2/README.md index a6e3572..73a90a7 100644 --- a/2024/day2/README.md +++ b/2024/day2/README.md @@ -38,3 +38,22 @@ So, in this example, `2` reports are **safe**. Analyze the unusual data from the engineers. **How many reports are safe?** ## Part 2 + +The engineers are surprised by the low number of safe reports until they realize they forgot to tell you about the Problem Dampener. + +The Problem Dampener is a reactor-mounted module that lets the reactor safety systems **tolerate a single bad level** in what would otherwise be a safe report. It's like the bad level never happened! + +Now, the same rules apply as before, except if removing a single level from an unsafe report would make it safe, the report instead counts as safe. + +More of the above example's reports are now safe: + +- `7 6 4 2 1`: **Safe** without removing any level. +- `1 2 7 8 9`: **Unsafe** regardless of which level is removed. +- `9 7 6 2 1`: **Unsafe** regardless of which level is removed. +- `1 3 2 4 5`: **Safe** by removing the second level, `3`. +- `8 6 4 4 1`: **Safe** by removing the third level, `4`. +- `1 3 6 7 9`: **Safe** without removing any level. + +Thanks to the Problem Dampener, `4` reports are actually **safe**! + +Update your analysis by handling situations where the Problem Dampener can remove a single level from unsafe reports. **How many reports are now safe?** diff --git a/2024/day2/index.php b/2024/day2/index.php index 0b1a7d7..bc9cf47 100644 --- a/2024/day2/index.php +++ b/2024/day2/index.php @@ -1,8 +1,8 @@ explode(' ', $line), $lines); @@ -10,45 +10,132 @@ function parse_reports(): array class Report { public function __construct( - public bool $allIncreasing, - public bool $allDecreasing, - public bool $safeDifference, - ) {} - + 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 || $this->allDecreasing); + return $this->safeDifference && ($this->allIncreasing xor $this->allDecreasing); } -} - -function get_report_safety (array $report): Report { - $allIncreasing = true; - $allDecreasing = true; - $safeDifference = true; - - $safeDiff = function ($curr, $prev): bool { - $diff = abs((int)$curr - (int)$prev); - - return $diff > 0 && $diff <= 3; - }; - $increasing = fn ($curr, $prev): bool => (int)$curr > (int)$prev; - $decreasing = fn ($curr, $prev): bool => (int)$curr < (int)$prev; - - for ($i = 1;$i < count($report); $i++) + + public function isSafeWithDampener(): bool { - $curr = $report[$i]; - $prev = $report[$i - 1]; - - $allIncreasing = $allIncreasing && $increasing($curr, $prev); - $allDecreasing = $allDecreasing && $decreasing($curr, $prev); - $safeDifference = $safeDifference && $safeDiff($curr, $prev); + 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; + } } - - return new Report($allIncreasing, $allDecreasing, $safeDifference); } -$reports = parse_reports(); -$reports = array_map('get_report_safety', $reports); -$safeReports = count(array_filter($reports, fn (Report $r) => $r->isSafe())); +// ---------------------------------------------------------------------------- -echo "Number of safe reports: $safeReports\n"; +$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";