|
1 | 1 | import Data.Char (isDigit)
|
2 | 2 | import Data.List (elemIndex, find)
|
3 | 3 | import Data.Map qualified as M
|
| 4 | +import Control.Arrow ((&&&)) |
4 | 5 |
|
5 | 6 | main :: IO ()
|
6 |
| -main = interact $ (++ "\n") . show . p1 . parse |
| 7 | +main = interact $ (++ "\n") . show . (p1 &&& p2) . parse |
7 | 8 |
|
8 | 9 | type Workflows = M.Map String [Rule]
|
9 | 10 | type Rule = (Maybe Condition, String)
|
@@ -40,3 +41,33 @@ valid ws p = go "in"
|
40 | 41 |
|
41 | 42 | p1 :: (Workflows, [Part]) -> Int
|
42 | 43 | p1 (workflows, parts) = sum . concat $ filter (valid workflows) parts
|
| 44 | + |
| 45 | +type Ranges = [(Int, Int)] |
| 46 | +type Thread = (Ranges, String) |
| 47 | + |
| 48 | +validCombinations :: Workflows -> Int |
| 49 | +validCombinations ws = go [(replicate 4 (1, 4000), "in")] |
| 50 | + where |
| 51 | + combo :: Ranges -> Int |
| 52 | + combo ranges = product $ map ((+1) . uncurry subtract) ranges |
| 53 | + rules w = ((M.!) ws w) |
| 54 | + go :: [Thread] -> Int |
| 55 | + go [] = 0 |
| 56 | + go ((rs, "A") : xs) = combo rs + go xs |
| 57 | + go ((_, "R") : xs) = go xs |
| 58 | + go ((rs, w) : xs) = go $ (splitThreads rs (rules w)) ++ xs |
| 59 | + splitThreads :: Ranges -> [Rule] -> [Thread] |
| 60 | + splitThreads rs ((Nothing, w) : _) = [(rs, w)] |
| 61 | + splitThreads rs ((Just c, w) : rest) = |
| 62 | + let (matching, notMatching) = split rs c |
| 63 | + in [(matching, w)] ++ splitThreads notMatching rest |
| 64 | + split :: Ranges -> Condition -> (Ranges, Ranges) |
| 65 | + split ranges (i, op, v) = foldl f ([], []) (zip [0..] ranges) |
| 66 | + where f (m, n) (j, r) | i == j = let (match, nomatch) = split' r op v |
| 67 | + in (m ++ [match], n ++ [nomatch]) |
| 68 | + | otherwise = (m ++ [r], n ++ [r]) |
| 69 | + split' (a, b) '<' v = ((a, v - 1), (v, b)) |
| 70 | + split' (a, b) '>' v = ((v + 1, b), (a, v)) |
| 71 | + |
| 72 | +p2 :: (Workflows, [Part]) -> Int |
| 73 | +p2 (workflows, _) = validCombinations workflows |
0 commit comments