@@ -28,6 +28,9 @@ module AOC.Challenge.Day12 (
28
28
29
29
import AOC.Prelude
30
30
31
+ import Data.Bitraversable
32
+ import Data.Finitary
33
+ import qualified AOC.Common.FinitarySet as FS
31
34
import qualified Data.Graph.Inductive as G
32
35
import qualified Data.IntMap as IM
33
36
import qualified Data.IntSet as IS
@@ -45,48 +48,114 @@ import qualified Text.Megaparsec as P
45
48
import qualified Text.Megaparsec.Char as P
46
49
import qualified Text.Megaparsec.Char.Lexer as PP
47
50
48
- -- False: small
49
- -- True: large
50
- toAdjMatrix :: [(String , String )] -> Map String [(Bool , String )]
51
- toAdjMatrix = fmap (mapMaybe postProcess)
52
- . M. fromListWith (++)
51
+ data SrcNode = StartNode
52
+ | SrcCave Cave
53
+ deriving stock (Generic , Eq , Ord , Show )
54
+ deriving anyclass (Finitary )
55
+
56
+ data DestNode = DestCave Cave
57
+ | EndNode
58
+ deriving stock (Generic , Eq , Ord , Show )
59
+ deriving anyclass (Finitary )
60
+
61
+ data FullNode = FullStart
62
+ | FullCave Cave
63
+ | FullEnd
64
+ deriving stock (Generic , Eq , Ord , Show )
65
+ deriving anyclass (Finitary )
66
+
67
+ data Ident = Ident (Finite 26 ) (Maybe (Finite 26 ))
68
+ deriving stock (Generic , Eq , Ord , Show )
69
+ deriving anyclass (Finitary )
70
+
71
+ data Cave = Big Ident
72
+ | Small Ident
73
+ deriving stock (Generic , Eq , Ord , Show )
74
+ deriving anyclass (Finitary )
75
+
76
+ classify :: String -> Maybe FullNode
77
+ classify " start" = Just FullStart
78
+ classify " end" = Just FullEnd
79
+ classify [a] = charFinite a <&> \ case
80
+ (False , x) -> FullCave $ Small (Ident x Nothing )
81
+ (True , x) -> FullCave $ Big (Ident x Nothing )
82
+ classify [a,b] = do
83
+ (cx, x) <- charFinite a
84
+ (cy, y) <- charFinite b
85
+ let ident = Ident x (Just y)
86
+ case (cx, cy) of
87
+ (False , False ) -> Just . FullCave $ Small ident
88
+ (True , True ) -> Just . FullCave $ Big ident
89
+ _ -> Nothing
90
+ classify _ = Nothing
91
+
92
+ srcDest :: FullNode -> (Maybe SrcNode , Maybe DestNode )
93
+ srcDest = \ case
94
+ FullStart -> (Just StartNode , Nothing )
95
+ FullCave c -> (Just (SrcCave c), Just (DestCave c))
96
+ FullEnd -> (Nothing , Just EndNode )
97
+
98
+ toAdjMatrix :: [(FullNode , FullNode )] -> Map SrcNode [DestNode ]
99
+ toAdjMatrix = M. fromListWith (++)
53
100
. concatMap (uncurry buildLinks)
54
101
where
55
- buildLinks a b = [(a, [b]), (b, [a])]
56
- postProcess str = (all isUpper str, str) <$ guard (str /= " start" )
102
+ toLink a b = (a, [b])
103
+ buildLinks a b = catMaybes
104
+ [ toLink <$> srcA <*> destB
105
+ , toLink <$> srcB <*> destA
106
+ ]
107
+ where
108
+ (srcA, destA) = srcDest a
109
+ (srcB, destB) = srcDest b
57
110
58
- day12a :: [(String , String )] :~> Int
111
+ day12a :: [(FullNode , FullNode )] :~> Int
59
112
day12a = MkSol
60
- { sParse = traverseLines $ listTup . splitOn " -"
113
+ { sParse = traverseLines $ bitraverse classify classify <=< listTup . splitOn " -"
61
114
, sShow = show
62
115
, sSolve = Just . length . findPaths . toAdjMatrix
63
116
}
64
117
65
- findPaths :: Map String [(Bool , String )] -> [[String ]]
66
- findPaths mp = go S. empty " start"
118
+ findPaths :: Map SrcNode [DestNode ] -> [[Cave ]]
119
+ findPaths mp = do
120
+ nextBranch <- mp M. ! StartNode
121
+ case nextBranch of
122
+ EndNode -> pure []
123
+ DestCave v@ (Big _) -> go FS. empty v
124
+ DestCave v@ (Small c) -> go (FS. singleton c) v
67
125
where
68
- go seen currPos
69
- | currPos == " end" = pure [" end" ]
70
- | otherwise = do
71
- (isLarge, nextBranch) <- mp M. ! currPos
72
- guard $ isLarge || (nextBranch `S.notMember` seen)
73
- (currPos: ) <$> go (S. insert nextBranch seen) nextBranch
74
-
75
- day12b :: [(String , String )] :~> Int
126
+ go :: FS. FinitarySet Ident -> Cave -> [[Cave ]]
127
+ go seen currPos = do
128
+ nextBranch <- mp M. ! SrcCave currPos
129
+ case nextBranch of
130
+ EndNode -> pure [currPos]
131
+ DestCave v@ (Big _) -> (currPos: ) <$> go seen v
132
+ DestCave v@ (Small c) -> do
133
+ guard $ c `FS.notMember` seen
134
+ (currPos: ) <$> go (c `FS.insert` seen) v
135
+
136
+ day12b :: [(FullNode , FullNode )] :~> Int
76
137
day12b = MkSol
77
138
{ sParse = sParse day12a
78
139
, sShow = show
79
140
, sSolve = Just . length . findPaths2 . toAdjMatrix
80
141
}
81
142
82
- findPaths2 :: Map String [(Bool , String )] -> [[String ]]
83
- findPaths2 mp = go S. empty Nothing " start"
143
+ findPaths2 :: Map SrcNode [DestNode ] -> [[Cave ]]
144
+ findPaths2 mp = do
145
+ nextBranch <- mp M. ! StartNode
146
+ case nextBranch of
147
+ EndNode -> pure []
148
+ DestCave v@ (Big _) -> go FS. empty Nothing v
149
+ DestCave v@ (Small c) -> go (FS. singleton c) Nothing v
84
150
where
85
- go seen seenTwice currPos
86
- | currPos == " end" = pure [" end" ]
87
- | otherwise = do
88
- (isLarge, nextBranch) <- mp M. ! currPos
89
- newSeenTwice <- if not isLarge && (nextBranch `S.member` seen)
90
- then Just nextBranch <$ guard (isNothing seenTwice)
151
+ go :: FS. FinitarySet Ident -> Maybe Ident -> Cave -> [[Cave ]]
152
+ go seen seenTwice currPos = do
153
+ nextBranch <- mp M. ! SrcCave currPos
154
+ case nextBranch of
155
+ EndNode -> pure [currPos]
156
+ DestCave v@ (Big _) -> (currPos: ) <$> go seen seenTwice v
157
+ DestCave v@ (Small c) -> do
158
+ newSeenTwice <- if c `FS.member` seen
159
+ then Just c <$ guard (isNothing seenTwice)
91
160
else pure seenTwice
92
- (currPos: ) <$> go (S . insert nextBranch seen) newSeenTwice nextBranch
161
+ (currPos: ) <$> go (c `FS .insert` seen) newSeenTwice v
0 commit comments