module Main where import Control.Monad import Control.Concurrent import Control.Concurrent.MVar import Control.Concurrent.STM --------------------- -- secrets primitives -- We assume that each secret is represent as a lower-case letter. -- Hence, we can simply use strings to represent secrets type Secret = String -- four secrets secretDB = ['a'..'d'] -- one of the two gets to know a new secret newSecrets s1 s2 = not $ and $ [elem x s1 | x <- s2] ++ [elem x s2 | x <- s1] shareSecrets :: Secret -> Secret -> Secret shareSecrets s1 s2 = s1 ++ s2 allSecrets :: String -> Bool allSecrets s = and $ [elem x s | x <- secretDB] data PhoneComm = PhoneComm { left :: MVar Secret, right :: MVar Secret } callOther name phonecomm secret = do b <- tryPutMVar (left phonecomm) secret if b then waitRight phonecomm else useRight phonecomm where waitRight pc = do othersSecret <- takeMVar (right pc) returnOrRetry othersSecret useRight pc = do putMVar (right pc) secret othersSecret <- takeMVar (left pc) returnOrRetry othersSecret returnOrRetry othersSecret = if newSecrets secret othersSecret then return $ shareSecrets secret othersSecret else callOther name phonecomm secret girl name initSecret phonecomm cnt out = let loop curSecret = do newSecret <- callOther name phonecomm curSecret writeChan out $ name ++ " " ++ curSecret ++ " " ++ newSecret when (allSecrets newSecret) $ do atomically $ do v <- readTVar cnt writeTVar cnt (v+1) writeChan out $ name ++ " Full" loop newSecret -- if a girl knows all secrets, other girls -- will still call her to obtain more secrets -- hence, we must attempt another call in loop initSecret printOutput o = do b <- isEmptyChan o when (not b) $ do w <- readChan o putStrLn w printOutput o main :: IO () main = do cnt <- newTVarIO 0 output <- newChan left <- newEmptyMVar right <- newEmptyMVar let phonecomm = PhoneComm left right mapM (\(n,s) -> forkIO $ girl n s phonecomm cnt output) [ ("Helga", "a"), ("Gertrud", "b"), ("Emmy", "c"), ("Ludmila", "d"), ("Karin", "a") ] -- each girl increments the counter if all secrets are known atomically $ do x <- readTVar cnt when (x < 5) retry putStr "Done" printOutput output -- just testing