A

Advent of code 2024 problem 2.

Posted on November 17, 2024

This is still a draft!

module Blogs.AOC2024_2 (solve1,solve2) where

import GHC.IO.IOMode (IOMode (ReadMode))
import System.IO (openFile)
import Text.Read (readMaybe)

newtype Report = Report [Int] deriving (Show)

data Direction = Increase | Decrease deriving (Show, Eq)

data Distance = One | Two | Three | Excessive deriving (Show, Eq)

parseReport :: String -> Maybe Report
parseReport input = Report <$> traverse readMaybe (words input)

parseReports :: String -> Maybe [Report]
parseReports input = traverse parseReport (lines input)

ints2Direction :: Int -> Int -> Direction
ints2Direction x y =
  if x < y
    then Increase
    else Decrease

ints2Distance :: Int -> Int -> Distance
ints2Distance x y
     | intDistance == 1 = One
     | intDistance == 2 = Two
     | intDistance == 3 = Three
     | otherwise = Excessive
  where
    intDistance = abs (x - y)

checkReportStart :: Report -> (Direction, Distance, Int, [Int])
checkReportStart (Report (x1 : x2 : tail)) =
  (ints2Direction x1 x2, ints2Distance x1 x2, x2, tail)
checkReportStart _ = error "Bad input!"

checkWith :: Direction -> Int -> [Int] -> Bool
checkWith direction _ [] = True
checkWith direction prev (next : tail) =
  let newDirection = ints2Direction prev next
      newDistance = ints2Distance prev next
   in direction == newDirection
        && (newDistance /= Excessive)
        && checkWith direction next tail

checkReport :: Report -> Bool
checkReport report =
  let (direction, distance, last, tail) = checkReportStart report
   in distance /= Excessive && checkWith direction last tail

countReports :: [Report] -> Int
countReports reports =
  foldr
    (\result acc -> if result then 1 + acc else acc)
    0
    (checkReport <$> reports)

extractOrFail :: Maybe a -> IO a
extractOrFail (Just x) = pure x
extractOrFail Nothing = error "Can't extract!"

solve1 :: IO ()
solve1 = do
  content <- readFile "AdventOfCode/Data/2024/2.txt"
  reports <- extractOrFail $ parseReports content
  let count = countReports reports
  -- the solution for my input! 585
  print count


removeItemN :: Int -> Report -> Report
removeItemN n (Report l) =
  let (start,end) = splitAt n l
  in
  case end of
    [] -> Report start
    (_:xs) -> Report (start++xs)

range :: Int -> [Int]
range n = if n<0 then [] else n : range (n-1)

len :: Report -> Int
len (Report x) = length x

tryRemovingN :: Report -> Int -> Bool
tryRemovingN report n =
  let newReport = removeItemN n report
      checked =  checkReport newReport
  in if checked then  checked else checked

checkReport2 :: Report -> Bool
checkReport2 report =
  let indexes = (reverse . range . len) report
  in
    any ((\ x y -> checkReport x || tryRemovingN x y) report) indexes


countReports2 :: [Report] -> Int
countReports2 reports =
  foldr
    (\result acc -> if result then 1 + acc else acc)
    0
    (checkReport2 <$> reports)


solve2 :: IO ()
solve2 = do
  content <- readFile "AdventOfCode/Data/2024/2.txt"
  reports <- extractOrFail $ parseReports content
  let count = countReports2 reports
  -- the solution for my input! 626
  print count