{-# LANGUAGE TemplateHaskell #-} import Control.Lens import Data.List import Data.List.Split import Data.Maybe data Monkey = Monkey { mID :: Int , _items :: [Int] , operation :: Int -> Int , test :: Int , nextMonkey :: (Int, Int) , _inspectCounter :: Int } makeLenses ''Monkey instance Show Monkey where show (Monkey a b c d (e, f) g) = "Monkey " ++ show a ++ ":\n Items: " ++ show b ++ "\n Operation: e.g. 1 -> " ++ show (c 1) ++ ", 2 -> " ++ show (c 2) ++ "\n Test: divisible by " ++ show d ++ "\n If true: throw to monkey " ++ show e ++ "\n If false: throw to monkey " ++ show f ++ "\nThis monkey has inspected " ++ show g ++ " items.\n" main :: IO () main = do input <- map (map (splitOn ":") . lines) . splitOn "\n\n" <$> readFile "input" let monkes = mapMaybe parseMonkey input print $ day11 20 (`div` 3) monkes print $ day11 10000 id monkes day11 :: Int -> (Int -> Int) -> [Monkey] -> Int day11 x f m = g $ take (succ x) $ iterate (turns f) m where g = product . take 2 . reverse . sort . map _inspectCounter . last parseMonkey :: [[String]] -> Maybe Monkey parseMonkey [[a, _], [_, b], [_, c], [_, d], [_, e], [_, f]] = Just $ Monkey (f' a) (read ('[' : b ++ "]")) (parseOP $ words c) (f' d) (f' e, f' f) 0 where f' = read . last . words parseOP [_, _, _, x, y] | y == "old" = (^ (2 :: Int)) | x == "*" = (*) (read y) | otherwise = (+) (read y) parseMonkey _ = Nothing turns :: (Int -> Int) -> [Monkey] -> [Monkey] turns f = turns' 0 where turns' n ms | length ms == n = ms | otherwise = turns' (succ n) monkeNew where itemsNew = [f $ flip mod modNum $ operation (ms !! n) x | x <- _items (ms !! n)] monkeEmptied = ms & ix n .~ ((ms !! n) & items .~ [] & inspectCounter +~ length itemsNew) monkeNew = monkeThrow itemsNew n monkeEmptied modNum = foldl1 lcm $ map test ms monkeThrow :: [Int] -> Int -> [Monkey] -> [Monkey] monkeThrow (x:xs) n ms = monkeThrow xs n (ms & ix next .~ ((ms !! next) & items .~ (_items (ms !! next) ++ [x]))) where next = if mod x (test (ms !! n)) == 0 then fst targets else snd targets targets = nextMonkey (ms !! n) monkeThrow _ _ ms = ms