packages feed

probable-0.1.1: examples/montyhall.hs

{-# LANGUAGE BangPatterns #-}
module Main where

import Data.List
import Math.Probable

-- | We have 3 distinct doors
data Door = D1 | D2 | D3
    deriving (Eq, Show)

-- | Our 3 doors in a list
doors :: [Door]
doors = [D1, D2, D3]

-- | We can either Win or Lose
data Result = Win | Lose
    deriving (Eq, Show)

-- | Knowing the door we have chosen so far
--   and the one that's been opened
--   we decide to keep the one we've picked
keep :: Door -> Door -> Door
keep chosen _ = chosen

-- | Knowing the door we have chosen so far
--   and the one that's been opened
--   we decide to switch to the third one
switch :: Door -> Door -> Door
switch chosen opened = head $ doors \\ [chosen, opened]

-- | Given one of the two functions above ("strategies"),
--   and (the chosen door, the already opened one, and 
--   the one with the car)
--   we check wether we won or not
resultOf :: (Door -> Door -> Door) -> Door -> Door -> Door -> Result
resultOf strategy chosen opened cardoor =
    case strategy chosen opened == cardoor of
        True  -> Win
        False -> Lose

-- | Given a strategy to adopt, what's the distribution of Win/Lose ?
result :: (Door -> Door -> Door)
       -> Fin Result
result strategy = do
    -- we pick a door uniformly for hiding the car
    carDoor    <- uniformly doors

    -- we pick a door uniformly for the player
    chosenDoor <- uniformly doors

    -- we open a door that neither hides the car (for suspense)
    -- nor the one the player has picked (but they can be the same door)
    openedDoor <- uniformly $ doors \\ [carDoor, chosenDoor]

    -- ok, the player tells us whether he decides to keep or switch
    -- we now check whether he wins or not
    let res = resultOf strategy chosenDoor openedDoor carDoor

    -- we return the result, to make this a distribution of results
    return res

-- | Given a strategy to adopt, distribution of doors for a Win ?
--   this uses Bayes' rule, and consequently lives in 'FinBayes'
result' :: (Door -> Door -> Door)
        -> FinBayes (Door, Door, Door)
result' strategy = do
    -- we pick a door uniformly for hiding the car
    carDoor    <- uniformly doors

    -- we pick a door uniformly for the player
    chosenDoor <- uniformly doors

    -- we open a door that neither hides the car (for suspense)
    -- nor the one the player has picked (but they can be the same door)
    openedDoor <- uniformly $ doors \\ [carDoor, chosenDoor]

    -- ok, the player tells us whether he decides to keep or switch
    -- we now check whether he wins or not
    let res = resultOf strategy chosenDoor openedDoor carDoor

    -- here we discard all the combinations that don't lead to Win
    condition (res == Win)

    -- and return the combination
    return (chosenDoor, openedDoor, carDoor)

main :: IO ()
main = do
    putStrLn $ "Using the conservative strategy: "
            ++ show (exact $ result keep)
    -- Using the conservative strategy: (Event Win 33.3%,Event Lose 66.7%)

    putStrLn $ "Switching: "
            ++ show (exact $ result switch)
    -- Switching: (Event Win 66.7%,Event Lose 33.3%)

    putStrLn "---"

    putStrLn $ "Winning (initial door, opened door, car door)'s - CONSERVATIVE:"
    mapM_ print . exact . bayes $ result' keep
    -- Event (D1,D2,D1) 16.7%
    -- Event (D1,D3,D1) 16.7%
    -- Event (D2,D1,D2) 16.7%
    -- Event (D2,D3,D2) 16.7%
    -- Event (D3,D1,D3) 16.7%
    -- Event (D3,D2,D3) 16.7%

    putStrLn "---"

    putStrLn $ "Winning (initial door, opened door, car door)'s - SWITCHING:"
    mapM_ print . exact . bayes $ result' switch
    -- Event (D2,D3,D1) 16.7%
    -- Event (D3,D2,D1) 16.7%
    -- Event (D1,D3,D2) 16.7%
    -- Event (D3,D1,D2) 16.7%
    -- Event (D1,D2,D3) 16.7%
    -- Event (D2,D1,D3) 16.7%