1+ #load " ticker.fsx"
2+ open System
3+ open Ticker
4+ open Hopac
5+
6+ type Reason =
7+ | Exploded
8+ | Defused
9+
10+ type Status =
11+ | NotActivated
12+ | Alive
13+ | Dead of Reason
14+
15+
16+ type TimeBomb () =
17+
18+ let reason = IVar< Reason>()
19+ let activated = IVar()
20+ let secondsRemainingCh = Ch< int>()
21+ let inCh = Ch< char>()
22+ let rec onTick ( ticker : Ticker ) secondsRemaining =
23+ ticker.C
24+ |> Alt.afterJob ( fun _ -> Ch.send secondsRemainingCh secondsRemaining)
25+ |> Alt.afterJob ( fun _ -> onTick ticker ( secondsRemaining - 1 ))
26+
27+ let startTicker seconds =
28+ let ticker = new Ticker( TimeSpan.FromSeconds 1. )
29+ onTick ticker ( seconds - 1 ) |> start
30+ ticker
31+
32+ let startTimeOut seconds =
33+ let timeOutAlt =
34+ seconds
35+ |> float
36+ |> TimeSpan.FromSeconds
37+ |> timeOut
38+
39+ timeOutAlt
40+ |> Alt.afterJob ( fun _ ->
41+ IVar.tryFill reason Exploded)
42+ |> start
43+
44+ let rec inputLoop defuseChar =
45+ inCh
46+ |> Alt.afterJob ( fun c ->
47+ if c = defuseChar then
48+ IVar.tryFill reason Defused
49+ else
50+ inputLoop defuseChar :> Job< unit>
51+ )
52+ let activate seconds defuseChar =
53+ let ticker = startTicker seconds
54+ startTimeOut seconds
55+ IVar.tryFill activated () |> start
56+ inputLoop defuseChar |> start
57+ IVar.read reason
58+ |> Alt.afterFun ( fun _ -> ticker.Stop())
59+
60+ member __.Status () =
61+ let deadReasonAlt =
62+ IVar.read reason
63+ |> Alt.afterFun Dead
64+
65+ let activatedAlt =
66+ IVar.read activated
67+ |> Alt.afterFun ( fun _ -> Alive)
68+
69+ let notActivatedAlt =
70+ Alt.always NotActivated
71+
72+ Alt.choose [
73+ deadReasonAlt
74+ activatedAlt
75+ notActivatedAlt
76+ ] |> run
77+
78+ member this.Activate ( seconds : int , defuseChar : char ) =
79+ match this.Status() with
80+ | NotActivated ->
81+ activate seconds defuseChar |> start
82+ | _ -> ()
83+
84+ member this.TryDefuse ( defuseChar ) =
85+ match this.Status() with
86+ | Alive ->
87+ Ch.give inCh defuseChar
88+ |> start
89+ | _ -> ()
90+
91+ member __.SecondsRemainingCh
92+ with get() = secondsRemainingCh
93+
94+ member __.DeadStatusAlt
95+ with get() = IVar.read reason
96+
97+
98+ let printSecondsRemaining ( t : TimeBomb ) =
99+ t.SecondsRemainingCh
100+ |> Alt.afterFun ( printfn " Seconds Remaining: %d " )
101+ |> Job.foreverServer |> start
102+
103+ let simulateExplosion () =
104+ let seconds = 5
105+ let t = TimeBomb()
106+ t.Status() |> printfn " Status: %A "
107+ t.Activate( seconds, 'a' )
108+ printSecondsRemaining t
109+ t.Status() |> printfn " Status: %A "
110+ t.DeadStatusAlt
111+ |> Alt.afterFun ( printfn " Status: %A " )
112+ |> run
113+
114+ let simulateDefusal char =
115+ let seconds = 5
116+ let t = TimeBomb()
117+ t.Status() |> printfn " Status: %A "
118+ t.Activate( seconds, 'a' )
119+ printSecondsRemaining t
120+ t.Status() |> printfn " Status: %A "
121+
122+ TimeSpan.FromSeconds 3.
123+ |> timeOut
124+ |> Alt.afterFun ( fun _ -> t.TryDefuse( char))
125+ |> Alt.afterJob ( fun _ -> t.DeadStatusAlt)
126+ |> Alt.afterFun ( printfn " Status: %A " )
127+ |> run
0 commit comments