2024-12-06 09:34:23 -05:00
|
|
|
<?php declare(strict_types=1);
|
|
|
|
|
2024-12-20 16:13:34 -05:00
|
|
|
function parse_reports(string $file = './input.txt'): array
|
2024-12-06 09:34:23 -05:00
|
|
|
{
|
2024-12-20 16:13:34 -05:00
|
|
|
$raw = file_get_contents($file);
|
2024-12-06 09:34:23 -05:00
|
|
|
$lines = explode("\n", trim($raw));
|
|
|
|
|
|
|
|
return array_map(fn (string $line) => explode(' ', $line), $lines);
|
|
|
|
}
|
|
|
|
|
2024-12-20 10:42:01 -05:00
|
|
|
class Report {
|
|
|
|
public function __construct(
|
2024-12-20 16:13:34 -05:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2024-12-20 10:42:01 -05:00
|
|
|
public function isSafe(): bool
|
|
|
|
{
|
2024-12-20 16:13:34 -05:00
|
|
|
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;
|
|
|
|
}
|
2024-12-20 10:42:01 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-20 16:13:34 -05:00
|
|
|
// ----------------------------------------------------------------------------
|
2024-12-20 10:42:01 -05:00
|
|
|
|
2024-12-20 16:13:34 -05:00
|
|
|
$testReports = array_map(fn (array $line) => new Report($line), parse_reports('./test_input.txt'));
|
2024-12-20 10:42:01 -05:00
|
|
|
|
2024-12-20 16:13:34 -05:00
|
|
|
$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');
|
2024-12-20 10:42:01 -05:00
|
|
|
|
2024-12-20 16:13:34 -05:00
|
|
|
$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);
|
2024-12-20 10:42:01 -05:00
|
|
|
|
|
|
|
|
2024-12-20 16:13:34 -05:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
$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);
|
2024-12-20 10:42:01 -05:00
|
|
|
|
2024-12-20 16:13:34 -05:00
|
|
|
$maybeValid = $dampenedSafeReports > 550
|
|
|
|
&& $dampenedSafeReports != 557
|
|
|
|
&& $dampenedSafeReports < 572;
|
2024-12-20 10:42:01 -05:00
|
|
|
|
2024-12-20 16:13:34 -05:00
|
|
|
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";
|