```module Numeric.Probability.Example.MontyHall where

import qualified Numeric.Probability.Distribution as Dist
import qualified Numeric.Probability.Transition as Trans
import Numeric.Probability.Simulation ((~.), )

import Numeric.Probability.Percentage
(Dist, RDist, Trans, )

import Data.List ( (\\) )

{- no Random instance for Rational
type Probability = Rational
type Dist a  = Dist.T  Probability a
type RDist a = Rnd.Distribution Probability a
type Trans a = Transition    Probability a
-}

data Door = A | B | C
deriving (Eq,Ord,Show)

doors :: [Door]
doors = [A,B,C]

data State = Doors {prize :: Door, chosen :: Door, opened :: Door}
deriving (Eq,Ord,Show)

-- | initial configuration of the game status
start :: State
start = Doors {prize=u,chosen=u,opened=u} where u=undefined

{- |
Steps of the game:

(1) hide the prize

(2) choose a door

(3) open a non-open door, not revealing the prize

(4) apply strategy: switch or stay
-}
hide :: Trans State
hide s = Dist.uniform [s {prize = d} | d <- doors]

choose :: Trans State
choose s = Dist.uniform [s {chosen = d} | d <- doors]

open :: Trans State
open s = Dist.uniform [s {opened = d} | d <- doors \\ [prize s,chosen s]]

type Strategy = Trans State

switch :: Strategy
switch s = Dist.uniform [s {chosen = d} | d <- doors \\ [chosen s,opened s]]

stay :: Strategy
stay = Trans.id

game :: Strategy -> Trans State

-- * Playing the game

data Outcome = Win | Lose
deriving (Eq,Ord,Show)

result :: State -> Outcome
result s = if chosen s==prize s then Win else Lose

eval :: Strategy -> Dist Outcome
eval s = Dist.map result (game s start)

simEval :: Int -> Strategy -> RDist Outcome
simEval k s = Dist.map result `fmap` (k ~. game s) start

-- * Alternative modeling

firstChoice :: Dist Outcome
firstChoice = Dist.uniform [Win,Lose,Lose]

switch' :: Trans Outcome
switch' Win  = Dist.certainly Lose
switch' Lose = Dist.certainly Win

-- * Play the game the monadic way

type StrategyM = Door -> Door -> Door

stayM :: StrategyM
stayM chosenDoor _openedDoor = chosenDoor

switchM :: StrategyM
switchM chosenDoor openedDoor =
let [finalDoor] = doors \\ [chosenDoor, openedDoor]
in  finalDoor

evalM :: StrategyM -> Dist Outcome
evalM chooseFinalDoor =
do prizeDoor  <- Dist.uniform doors
chosenDoor <- Dist.uniform doors
openedDoor <- Dist.uniform (doors \\ [prizeDoor, chosenDoor])
return (if chooseFinalDoor chosenDoor openedDoor == prizeDoor
then Win else Lose)
```