executable advent15
import: common-extensions, build-directives
main-is: advent15/Main.hs
- build-depends: containers, text, attoparsec,
+ build-depends: containers, text, attoparsec
executable advent16
import: common-extensions, build-directives
import: common-extensions, build-directives
main-is: advent17/Main.hs
build-depends: containers, linear, array, pqueue, mtl, lens
+
+executable advent18
+ import: common-extensions, build-directives
+ main-is: advent18/Main.hs
+ build-depends: linear, text, attoparsec
+
\ No newline at end of file
--- /dev/null
+-- Writeup at https://work.njae.me.uk/2023/12/18/advent-of-code-2023-day-15/
+
+import AoC
+
+import Data.Text (Text)
+import qualified Data.Text.Read as TR
+import qualified Data.Text.IO as TIO
+import Data.Attoparsec.Text hiding (D)
+import qualified Data.Attoparsec.Text as AT
+-- import Control.Applicative
+import Linear (V2(..), (^+^), (^*))
+import Data.List
+import Data.Char
+import Data.Either
+
+
+data Direction = U | D | L | R deriving (Show, Eq, Ord)
+data Instruction = Instr Direction Int deriving (Show, Eq, Ord)
+
+type Position = V2 Int -- x, y
+
+main :: IO ()
+main =
+ do dataFileName <- getDataFileName
+ text <- TIO.readFile dataFileName
+ print $ part1 text
+ print $ part2 text
+
+part1 :: Text -> Int
+part1 = area . successfulParse instructions1P
+
+part2 :: Text -> Int
+part2 = area . successfulParse instructions2P
+
+area :: [Instruction] -> Int
+area instrs = (shoelace $ mkTrench instrs) + ((perimeter instrs) `div` 2) + 1
+
+shoelace :: [Position] -> Int
+shoelace ps = (abs $ sum $ zipWith mulPair ps $ tail ps) `div` 2
+ where mulPair (V2 x1 y1) (V2 x2 y2) = x1 * y2 - x2 * y1
+
+perimeter :: [Instruction] -> Int
+perimeter = sum . fmap (\(Instr _ len) -> len)
+
+mkTrench :: [Instruction] -> [Position]
+mkTrench = scanl' move (V2 0 0)
+ where move here (Instr dir len) = here ^+^ (delta dir ^* len)
+
+delta :: Direction -> V2 Int
+delta U = V2 0 1
+delta D = V2 0 (-1)
+delta L = V2 (-1) 0
+delta R = V2 1 0
+
+-- Parse the input file
+
+instructions1P, instructions2P :: Parser [Instruction]
+instruction1P, instruction2P :: Parser Instruction
+direction1P, direction2P :: Parser Direction
+preambleP :: Parser Int
+
+instructions1P = instruction1P `sepBy` endOfLine
+instruction1P = Instr <$> (direction1P <* " ")
+ <*> (decimal <* " ")
+ <* (skipWhile (not . isSpace))
+
+direction1P = choice [ U <$ "U"
+ , D <$ "D"
+ , L <$ "L"
+ , R <$ "R"
+ ]
+
+direction2P = choice [ U <$ "3"
+ , D <$ "1"
+ , L <$ "2"
+ , R <$ "0"
+ ]
+
+instructions2P = instruction2P `sepBy` endOfLine
+
+instruction2P =
+ instrify <$> (preambleP *> (AT.take 5)) <*> (direction2P <* ")")
+
+preambleP = (direction1P *> " " *> decimal <* " (#")
+
+instrify :: Text -> Direction -> Instruction
+instrify h d = Instr d (fst $ fromRight (0, "") $ TR.hexadecimal h)
+
+successfulParse :: Parser [Instruction] -> Text -> [Instruction]
+successfulParse parser input =
+ case parseOnly parser input of
+ Left _err -> [] -- TIO.putStr $ T.pack $ parseErrorPretty err
+ Right matches -> matches
\ No newline at end of file