diff --git a/hs/src/Day2.hs b/hs/src/Day2.hs index 69b58c4..dfb85cf 100644 --- a/hs/src/Day2.hs +++ b/hs/src/Day2.hs @@ -4,8 +4,10 @@ module Day2 (part1, part2) where import Common (readEntire) -import Data.Ix (inRange) +import Control.Monad (ap, foldM_, guard) +import Data.Functor (($>)) import Data.List (inits, tails) +import Data.Maybe (isJust) import Data.Text (Text) import Data.Text qualified as T (lines, words) import Data.Text.Read qualified as T (decimal) @@ -13,14 +15,14 @@ import Data.Text.Read qualified as T (decimal) parse :: Text -> Either String [[Int]] parse = mapM (mapM (readEntire T.decimal) . T.words) . T.lines -isSafe, isSafe' :: [Int] -> Bool -isSafe report = all (inRange (-3, -1)) delta || all (inRange (1, 3)) delta +isSafe1, isSafe2 :: [Int] -> Bool +isSafe1 = isJust . foldM_ go EQ . (zipWith (-) `ap` drop 1) where - delta = zipWith (-) report $ drop 1 report -isSafe' report = any isSafe [a ++ b | (a, _ : b) <- zip (inits report) (tails report)] + go k x = guard (x /= 0 && abs x <= 3 && k /= compare 0 x) $> compare x 0 +isSafe2 report = any isSafe1 [a ++ b | (a, _ : b) <- zip (inits report) (tails report)] part1 :: Text -> Either String Int -part1 input = length . filter isSafe <$> parse input +part1 input = length . filter isSafe1 <$> parse input part2 :: Text -> Either String Int -part2 input = length . filter isSafe' <$> parse input +part2 input = length . filter isSafe2 <$> parse input diff --git a/kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day2.kt b/kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day2.kt index f700f05..1a1d388 100644 --- a/kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day2.kt +++ b/kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day2.kt @@ -16,24 +16,22 @@ class Day2(input: String) { private fun isSafe1(report: IntArray): Boolean { var decreasing = false var increasing = false - for (i in 0..report.size - 2) { - when (report[i + 1] - report[i]) { - in -3..-1 -> decreasing = true - in 1..3 -> increasing = true - else -> return false + return (0..report.size - 2).all { + when (report[it + 1] - report[it]) { + in -3..-1 -> !increasing.also { decreasing = true } + in 1..3 -> !decreasing.also { increasing = true } + else -> false } } - return !(decreasing && increasing) } private fun isSafe2(report: IntArray): Boolean { if (report.isEmpty()) return true - val report2 = report.copyOf(report.size - 1) - for (i in report2.lastIndex downTo 0) { - if (isSafe1(report2)) return true - report2[i] = report[i + 1] + val report2 = report.copyOfRange(1, report.size) + return isSafe1(report2) || report2.indices.any { + report2[it] = report[it] + isSafe1(report2) } - return isSafe1(report2) } } } diff --git a/rs/src/day2.rs b/rs/src/day2.rs index e8dd701..2542e34 100644 --- a/rs/src/day2.rs +++ b/rs/src/day2.rs @@ -1,30 +1,25 @@ -fn parse(data: &str) -> Vec> { - data.lines() - .flat_map(|line| { - line.split_ascii_whitespace() - .map(|level| level.parse::().ok()) - .collect::>>() - }) - .collect::>() +use std::cmp::Ordering; + +fn parse(data: &str) -> impl Iterator> + use<'_> { + data.lines().flat_map(|line| { + line.split_ascii_whitespace() + .map(|level| level.parse::().ok()) + .collect::>>() + }) } fn is_safe_1(report: &[i32]) -> bool { - let mut decreasing = false; - let mut increasing = false; report .iter() .zip(report.iter().skip(1)) - .all(|(x, y)| match x - y { - -3..=-1 => { - decreasing = true; - !increasing - } - 1..=3 => { - increasing = true; - !decreasing + .try_fold(Ordering::Equal, |cmp, (x, y)| { + if x != y && (x - y).abs() <= 3 { + Some(x.cmp(y)).filter(|cmp2| cmp != cmp2.reverse()) + } else { + None } - _ => false, }) + .is_some() } fn is_safe_2(report: &[i32]) -> bool { @@ -41,17 +36,11 @@ fn is_safe_2(report: &[i32]) -> bool { } pub fn part1(data: &str) -> usize { - parse(data) - .into_iter() - .filter(|report| is_safe_1(report)) - .count() + parse(data).filter(|report| is_safe_1(report)).count() } pub fn part2(data: &str) -> usize { - parse(data) - .into_iter() - .filter(|report| is_safe_2(report)) - .count() + parse(data).filter(|report| is_safe_2(report)).count() } #[cfg(test)]