content: -- |
  -- Module      : AOC.Challenge.Day03
  -- License     : BSD3
  --
  -- Stability   : experimental
  -- Portability : non-portable
  --
  -- Day 3.  See "AOC.Solver" for the types used in this module!
  
  module AOC.Challenge.Day03 (
      day03a
    , day03b
    ) where
  
  import           AOC.Common         (Point, mannDist, Dir, dirPoint, parseDir)
  import           AOC.Solver         ((:~>)(..))
  import           Control.Monad      ((<=<))
  import           Data.List.NonEmpty (NonEmpty(..))
  import           Data.List.Split    (splitOn)
  import           Data.Map           (Map)
  import           Data.Semigroup     (Min(..))
  import           Safe               (scanl1Def)
  import           Safe.Foldable      (minimumMay)
  import           Text.Read          (readMaybe)
  import qualified Data.List.NonEmpty as NE
  import qualified Data.Map           as M
  
  type Path = [(Dir, Int)]
  
  parsePath :: String -> Maybe Path
  parsePath = traverse parsePoint . splitOn ","
    where
      parsePoint (d:ns) = (,) <$> parseDir d <*> readMaybe ns
      parsePoint _      = Nothing
  
  -- | From a list of paths, get a Map of the points where they cross, along
  -- with the minimum time sum to get to that point.
  crossings :: NonEmpty Path -> Map Point Int
  crossings = foldr1 (M.intersectionWith (+)) . fmap follow
    where
      -- a map of every point visted to the steps taken to visit it
      follow :: Path -> Map Point Int
      follow = M.fromListWith min
             . flip zip [1..]
             . scanl1Def [] (+)
             . concatMap (uncurry expandDir)
      expandDir d ns = replicate ns (dirPoint d)
  
  day03a :: NonEmpty Path :~> Int
  day03a = MkSol
      { sParse = NE.nonEmpty <=< traverse parsePath . lines
      , sShow  = show
      , sSolve = fmap getMin
               . foldMap (Just . Min . mannDist 0)
               . M.keys
               . crossings
      }
  
  day03b :: NonEmpty Path :~> Int
  day03b = MkSol
      { sParse = NE.nonEmpty <=< traverse parsePath . lines
      , sShow  = show
      , sSolve = minimumMay . crossings
      }
  