aboutsummaryrefslogtreecommitdiffstats
path: root/day12/main.hs
blob: 50402e66c98f5b9602c8a75914d3dbc091bbcef2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
module Main where

import Data.Char (isLower, isUpper)
import Data.Function (on)
import Data.List (concatMap, groupBy, intercalate, nub, sort, sortOn)
import Data.Map.Strict (Map, (!))
import qualified Data.Map.Strict as Map
import Data.Text (pack, split, unpack)
import Debug.Trace (traceShow)
import Utils

newtype Cave = Cave String

instance Eq Cave where
  (==) (Cave a) (Cave b) = a == b

instance Ord Cave where
  compare (Cave a) (Cave b) = compare a b

instance Show Cave where
  show (Cave cave) = show cave

newtype Graph = Graph (Map Cave [Cave])

instance Show Graph where
  show (Graph hmap) = show hmap

newtype Path = Path [Cave]

instance Show Path where
  show (Path caves) = caves $> map (\(Cave s) -> s) .> intercalate ","

neighbors (Graph hmap) c = hmap ! c

groupCavePairs :: [(String, String)] -> [(Cave, [Cave])]
groupCavePairs =
  sortOn fst
    .> groupBy ((==) `on` fst)
    .> map (\lst -> (Cave $ fst (head lst), map (snd .> Cave) lst))

toPair [a, b] = (a, b)
toPair other = error $ "invalid list: " ++ show other

flipPair (a, b) = (b, a)

isSmall (Cave cave) = isLower (head cave)

isBig (Cave cave) = isUpper (head cave)

isStart (Cave cave) = cave == "start"

isEnd (Cave cave) = cave == "end"

walk1 :: Graph -> Path -> Cave -> [Path]
walk1 graph (Path path) c
  | isEnd c = [Path $ reverse (c : path)]
  | c `elem` path && isSmall c = []
  | otherwise =
    let path' = Path $ c : path
        ns = neighbors graph c
     in concatMap (walk1 graph path') ns

e1 :: Graph -> [Path]
e1 graph = walk1 graph (Path []) (Cave "start")

countBy f = filter f .> length

count lst el = filter (== el) lst $> length

walk2 :: Graph -> Path -> Bool -> Cave -> Int
walk2 graph p@(Path path) smallVisitedTwice c@(Cave cName)
  | isEnd c = 1
  | isStart c && c `elem` path = 0
  | isSmall c && c `elem` path && smallVisitedTwice = 0
  | isSmall c && c `elem` path && not smallVisitedTwice =
    let path' = Path $ c : path
        ns = neighbors graph c
     in sum $ map (walk2 graph path' True) ns
  | otherwise =
    let path' = Path $ c : path
        ns = neighbors graph c
     in sum $ map (walk2 graph path' smallVisitedTwice) ns

e2 graph = walk2 graph (Path []) False (Cave "start")

main :: IO ()
main =
  do
    contents <- getContents

    let input =
          contents
            $> lines
            .> map (pack .> split (== '-') .> map unpack .> toPair)
            .> concatMap (\p -> [p, flipPair p])

    let graph = Graph $ Map.fromList $ groupCavePairs input

    let result1 = e1 graph
    result1 $> length .> print
    let result2 = e2 graph
    result2 $> print