diff --git a/2022/day11.hs b/2022/day11.hs new file mode 100644 index 0000000..f1419e5 --- /dev/null +++ b/2022/day11.hs @@ -0,0 +1,90 @@ +import Data.List.Split (splitOn) +import qualified Control.Monad.Trans.State as St +import qualified Data.Map as M +import Control.Monad (replicateM_) +import Data.List (sort) + +data Expr = Old | Lit Int | Add Expr Expr | Mul Expr Expr +data Test = T Int Int Int +data Part = Part1 | Part2 +data Monkey = M [Int] Expr Test + +getFactor :: Monkey -> Int +getFactor (M _ _ (T x _ _)) = x + +parseElem :: String -> Expr +parseElem "old" = Old +parseElem i = Lit (read i) + +parseExpr :: [String] -> Expr +parseExpr s + | s !! 1 == "*" = Mul (parseElem $ s !! 0) (parseElem $ s !! 2) + | s !! 1 == "+" = Add (parseElem $ s !! 0) (parseElem $ s !! 2) + | otherwise = error $ "unknown expression: " ++ concat s + +parseMonkey :: [String] -> Monkey +parseMonkey s = M startingItems expr test where + startingItems = read $ "[" ++ (splitOn ": " (s !! 1) !! 1) ++ "]" + expr = parseExpr $ last $ splitOn ["="] (words (s !! 2)) + testFactor = read $ last $ words (s !! 3) + testTargetT = read $ last $ words (s !! 4) + testTargetF = read $ last $ words (s !! 5) + test = T testFactor testTargetT testTargetF + +evalExpr :: Expr -> Int -> Int +evalExpr Old x = x +evalExpr (Lit i) x = i +evalExpr (Add a b) x = evalExpr a x + evalExpr b x +evalExpr (Mul a b) x = evalExpr a x * evalExpr b x + +type Monkeys = M.Map Int (Monkey, Int) + +steps :: Part -> Int -> Monkey -> St.State Monkeys (Int, Monkey) +steps part factor monkey@(M [] _ _) = do + pure (0, monkey) +steps part factor (M (item:items) op test@(T fac tr fa)) = do + let newItem' = evalExpr op item --`div` 3 + let newItem = case part of + Part1 -> evalExpr op item `div` 3 + Part2 -> evalExpr op item `mod` factor + let targetMonkey = if newItem `mod` fac == 0 then tr else fa + monkeys <- St.get + let (M items' op' test', count') = monkeys M.! targetMonkey + let updated = M (newItem:items') op' test' + St.put $ M.insert targetMonkey (updated, count') monkeys + (count, final) <- steps part factor $ M items op test + pure (count+1, final) + +simulate :: Part -> Int -> Int -> St.State Monkeys () +simulate part factor i = do + monkeys <- St.get + let (monkey, count) = monkeys M.! i + (iters, finished) <- steps part factor monkey + monkeys <- St.get + St.put $ M.insert i (finished, count + iters) monkeys + +round :: Part -> Int -> St.State Monkeys () +round part factor = do + monkeys <- St.get + mapM_ (simulate part factor . fst) (M.toAscList monkeys) + +main :: IO () +main = do + monkeys <- map parseMonkey . splitOn [""] . lines <$> getContents + let factor = foldr1 lcm $ map getFactor monkeys + let initialState = M.fromAscList $ zip [0..] (map (\x -> (x,0)) monkeys) + + putStr "part 1: " + let action = replicateM_ 20 (Main.round Part1 factor) + let results = St.execState action initialState + let counts = map (snd . snd) (M.toAscList results) + print $ counts + print $ product $ take 2 $ reverse $ sort counts + + putStr "part 2: " + let action = replicateM_ 10000 (Main.round Part2 factor) + let results = St.execState action initialState + let counts = map (snd . snd) (M.toAscList results) + print $ counts + print $ product $ take 2 $ reverse $ sort counts + diff --git a/2022/inputs/day11 b/2022/inputs/day11 new file mode 100644 index 0000000..7b193fa --- /dev/null +++ b/2022/inputs/day11 @@ -0,0 +1,55 @@ +Monkey 0: + Starting items: 84, 66, 62, 69, 88, 91, 91 + Operation: new = old * 11 + Test: divisible by 2 + If true: throw to monkey 4 + If false: throw to monkey 7 + +Monkey 1: + Starting items: 98, 50, 76, 99 + Operation: new = old * old + Test: divisible by 7 + If true: throw to monkey 3 + If false: throw to monkey 6 + +Monkey 2: + Starting items: 72, 56, 94 + Operation: new = old + 1 + Test: divisible by 13 + If true: throw to monkey 4 + If false: throw to monkey 0 + +Monkey 3: + Starting items: 55, 88, 90, 77, 60, 67 + Operation: new = old + 2 + Test: divisible by 3 + If true: throw to monkey 6 + If false: throw to monkey 5 + +Monkey 4: + Starting items: 69, 72, 63, 60, 72, 52, 63, 78 + Operation: new = old * 13 + Test: divisible by 19 + If true: throw to monkey 1 + If false: throw to monkey 7 + +Monkey 5: + Starting items: 89, 73 + Operation: new = old + 5 + Test: divisible by 17 + If true: throw to monkey 2 + If false: throw to monkey 0 + +Monkey 6: + Starting items: 78, 68, 98, 88, 66 + Operation: new = old + 6 + Test: divisible by 11 + If true: throw to monkey 2 + If false: throw to monkey 5 + +Monkey 7: + Starting items: 70 + Operation: new = old + 7 + Test: divisible by 5 + If true: throw to monkey 1 + If false: throw to monkey 3 diff --git a/2022/inputs/day11.test b/2022/inputs/day11.test new file mode 100644 index 0000000..30e09e5 --- /dev/null +++ b/2022/inputs/day11.test @@ -0,0 +1,27 @@ +Monkey 0: + Starting items: 79, 98 + Operation: new = old * 19 + Test: divisible by 23 + If true: throw to monkey 2 + If false: throw to monkey 3 + +Monkey 1: + Starting items: 54, 65, 75, 74 + Operation: new = old + 6 + Test: divisible by 19 + If true: throw to monkey 2 + If false: throw to monkey 0 + +Monkey 2: + Starting items: 79, 60, 97 + Operation: new = old * old + Test: divisible by 13 + If true: throw to monkey 1 + If false: throw to monkey 3 + +Monkey 3: + Starting items: 74 + Operation: new = old + 3 + Test: divisible by 17 + If true: throw to monkey 0 + If false: throw to monkey 1