7cccd05d3e9784c5148d44496340fcd20699825e
[advent-of-code-23.git] / advent18 / Main.hs
1 -- Writeup at https://work.njae.me.uk/2023/12/18/advent-of-code-2023-day-15/
2
3 import AoC
4
5 import Data.Text (Text)
6 import qualified Data.Text.Read as TR
7 import qualified Data.Text.IO as TIO
8 import Data.Attoparsec.Text hiding (D)
9 import qualified Data.Attoparsec.Text as AT
10 -- import Control.Applicative
11 import Linear (V2(..), (^+^), (^*))
12 import Data.List
13 import Data.Char
14 import Data.Either
15
16
17 data Direction = U | D | L | R deriving (Show, Eq, Ord)
18 data Instruction = Instr Direction Int deriving (Show, Eq, Ord)
19
20 type Position = V2 Int -- x, y
21
22 main :: IO ()
23 main =
24 do dataFileName <- getDataFileName
25 text <- TIO.readFile dataFileName
26 print $ part1 text
27 print $ part2 text
28
29 part1 :: Text -> Int
30 part1 = area . successfulParse instructions1P
31
32 part2 :: Text -> Int
33 part2 = area . successfulParse instructions2P
34
35 area :: [Instruction] -> Int
36 area instrs = (shoelace $ mkTrench instrs) + ((perimeter instrs) `div` 2) + 1
37
38 shoelace :: [Position] -> Int
39 shoelace ps = (abs $ sum $ zipWith mulPair ps $ tail ps) `div` 2
40 where mulPair (V2 x1 y1) (V2 x2 y2) = x1 * y2 - x2 * y1
41
42 perimeter :: [Instruction] -> Int
43 perimeter = sum . fmap (\(Instr _ len) -> len)
44
45 mkTrench :: [Instruction] -> [Position]
46 mkTrench = scanl' move (V2 0 0)
47 where move here (Instr dir len) = here ^+^ (delta dir ^* len)
48
49 delta :: Direction -> V2 Int
50 delta U = V2 0 1
51 delta D = V2 0 (-1)
52 delta L = V2 (-1) 0
53 delta R = V2 1 0
54
55 -- Parse the input file
56
57 instructions1P, instructions2P :: Parser [Instruction]
58 instruction1P, instruction2P :: Parser Instruction
59 direction1P, direction2P :: Parser Direction
60 preambleP :: Parser Int
61
62 instructions1P = instruction1P `sepBy` endOfLine
63 instruction1P = Instr <$> (direction1P <* " ")
64 <*> (decimal <* " ")
65 <* (skipWhile (not . isSpace))
66
67 direction1P = choice [ U <$ "U"
68 , D <$ "D"
69 , L <$ "L"
70 , R <$ "R"
71 ]
72
73 direction2P = choice [ U <$ "3"
74 , D <$ "1"
75 , L <$ "2"
76 , R <$ "0"
77 ]
78
79 instructions2P = instruction2P `sepBy` endOfLine
80
81 instruction2P =
82 instrify <$> (preambleP *> (AT.take 5)) <*> (direction2P <* ")")
83
84 preambleP = (direction1P *> " " *> decimal <* " (#")
85
86 instrify :: Text -> Direction -> Instruction
87 instrify h d = Instr d (fst $ fromRight (0, "") $ TR.hexadecimal h)
88
89 successfulParse :: Parser [Instruction] -> Text -> [Instruction]
90 successfulParse parser input =
91 case parseOnly parser input of
92 Left _err -> [] -- TIO.putStr $ T.pack $ parseErrorPretty err
93 Right matches -> matches