1 import Data.Text (Text)
2 import qualified Data.Text.IO as TIO
4 import Data.Void (Void)
6 import Text.Megaparsec hiding (State)
7 import Text.Megaparsec.Char
8 import qualified Text.Megaparsec.Char.Lexer as L
9 import qualified Control.Applicative as CA
11 import Data.List (foldl')
12 import qualified Data.Set as S
14 import Linear (V2(..), (^+^), (^-^), (*^), (*^))
16 data Direction = East | South | West | North deriving (Show, Eq)
18 type Location = V2 Int -- x, y
20 type Visited = S.Set Location
22 data Path = Path { _visited :: Visited
27 data Segment = Segment { _direction :: Direction
34 text <- TIO.readFile "data/advent03.txt"
35 let segmentss = successfulParse text
37 -- print $ travelPath $ head segmentss
38 print $ part1 segmentss
39 -- print $ part2 machine
41 part1 :: [[Segment]] -> Int
42 part1 segmentss = closest $ crossovers $ travelAllPaths segmentss
44 closest :: Visited -> Int
45 closest points = S.findMin $ S.map manhattan points
47 crossovers :: [Path] -> Visited
48 crossovers travelledPaths =
50 (_visited $ head travelledPaths)
51 (map _visited $ drop 1 travelledPaths)
53 travelAllPaths :: [[Segment]] -> [Path]
54 travelAllPaths = map travelPath
56 travelPath :: [Segment] -> Path
57 travelPath segments = foldl' travelSegment path0 segments
58 where path0 = Path { _visited = S.empty, _tip = V2 0 0 }
60 travelSegment :: Path -> Segment -> Path
61 travelSegment path segment = path { _tip = tip', _visited = visited' }
62 where delta = facing $ _direction segment
63 distance = _steps segment
65 visited = _visited path
66 visited' = foldl' (flip S.insert) visited $ take distance $ drop 1 $ iterate (^+^ delta) start
67 tip' = start ^+^ distance *^ delta
69 facing :: Direction -> Location
71 facing South = V2 0 (-1)
72 facing West = V2 (-1) 0
76 manhattan (V2 x y) = (abs x) + (abs y)
78 -- Parse the input file
79 type Parser = Parsec Void Text
82 sc = L.space (skipSome spaceChar) CA.empty CA.empty
83 -- sc = L.space (skipSome (char ' ')) CA.empty CA.empty
86 integer = lexeme L.decimal
87 -- signedInteger = L.signed sc integer
92 pathP = segmentP `sepBy1` comma
94 segmentP = segmentify <$> directionP <*> integer
95 where segmentify direction steps =
96 Segment { _direction = direction, _steps = steps }
99 directionP = eP <|> sP <|> wP <|> nP
100 eP = (symb "R" *> pure East)
101 sP = (symb "D" *> pure South)
102 wP = (symb "L" *> pure West)
103 nP = (symb "U" *> pure North)
106 successfulParse :: Text -> [[Segment]]
107 successfulParse input =
108 case parse wiresP "input" input of
109 Left _err -> [] -- TIO.putStr $ T.pack $ parseErrorPretty err