executable advent08
import: common-extensions, build-directives
main-is: advent08/Main.hs
- -- build-depends: text, attoparsec, containers, path-tree, rosezipper
+
+executable advent09
+ import: common-extensions, build-directives
+ main-is: advent09/Main.hs
+ build-depends: text, attoparsec, containers, linear, lens
--- /dev/null
+-- Writeup at https://work.njae.me.uk/2022/12/04/advent-of-code-2022-day-4/
+
+import AoC
+import Data.Text (Text)
+import qualified Data.Text.IO as TIO
+import Data.Attoparsec.Text hiding (take, D)
+import Control.Applicative
+import Data.List
+import qualified Data.Set as S
+import Linear hiding (Trace, trace, distance)
+import Control.Lens
+
+type Position = V2 Int
+type Trace = S.Set Position
+type Path = [Position]
+
+data Rope = Rope
+ { _headR :: Position
+ , _tailR :: Position
+ , _trace :: Trace
+ } deriving (Show, Eq)
+makeLenses ''Rope
+
+data Direction = U Int | R Int | D Int | L Int
+ deriving (Show, Eq, Ord)
+
+main :: IO ()
+main =
+ do dataFileName <- getDataFileName
+ text <- TIO.readFile dataFileName
+ let path = successfulParse text
+ let steps = expandPath path
+ print $ part1 steps
+
+part1 :: Path -> Int
+part1 steps = S.size $ rope' ^. trace
+ where rope' = ropeSteps newRope steps
+
+expandPath :: [Direction] -> Path
+expandPath = concatMap expandStep
+ where expandStep (U n) = replicate n (V2 0 1)
+ expandStep (L n) = replicate n (V2 -1 0)
+ expandStep (D n) = replicate n (V2 0 -1)
+ expandStep (R n) = replicate n (V2 1 0)
+
+
+manhattan :: Position -> Position -> Int
+manhattan p1 p2 = max dx dy
+ where V2 dx dy = abs $ p1 ^-^ p2
+
+touching :: Position -> Position -> Bool
+touching p1 p2 = (manhattan p1 p2) <= 1
+
+towards :: Position -> Position -> Position
+towards p1 p2 = signum $ p2 ^-^ p1
+
+newRope :: Rope
+newRope = Rope { _headR = V2 0 0, _tailR = V2 0 0, _trace = S.singleton (V2 0 0) }
+
+ropeSteps :: Rope -> Path -> Rope
+ropeSteps rope steps = foldl' ropeStep rope steps
+
+ropeStep :: Rope -> Position -> Rope
+ropeStep rope step = rope & headR .~ hr
+ & tailR .~ tailR'
+ & trace %~ S.insert tailR'
+ where hr = (rope ^. headR) ^+^ step
+ tr = rope ^. tailR
+ tailR' = if tr `touching` hr
+ then rope ^. tailR
+ else tr ^+^ (tr `towards` hr)
+
+-- Parse the input file
+
+pathP :: Parser [Direction]
+directionP, upP, leftP, downP, rightP :: Parser Direction
+
+pathP = directionP `sepBy` endOfLine
+directionP = upP <|> leftP <|> downP <|> rightP
+upP = U <$> ("U " *> decimal)
+leftP = L <$> ("L " *> decimal)
+downP = D <$> ("D " *> decimal)
+rightP = R <$> ("R " *> decimal)
+
+
+successfulParse :: Text -> [Direction]
+successfulParse input =
+ case parseOnly pathP input of
+ Left _err -> [] -- TIO.putStr $ T.pack $ parseErrorPretty err
+ Right path -> path