Use milliseconds to update ICS clocks.
[kaya.git] / lib / clock.rb
blob11104f2baeb571f4295a4786fb9d6055c212ab05
1 require 'observer_utils'
2 require 'qtutils'
4 class Clock
5   include Observable
6   ByoYomi = Struct.new(:time, :periods)
8   # main = allotted time
9   # increment = increment per move
10   # byoyomi.time = time per move after main is elapsed
11   # byoyomi.periods = number of times byoyomi.time has to elapse
12   #                   for the byoyomi to end
13   #                   
14   # byoyomi and increment don't work together
15   #                   
16   # all times are in seconds
17   # 
18   def initialize(main, increment, byoyomi = nil, timer_class = Qt::Timer)
19     @main = main
20     @increment = increment
21     if byoyomi
22       @byoyomi = byoyomi.dup
23       @byoyomi_time = @byoyomi.time
24     end
25     
26     @timer = timer_class.new
27     @timer.single_shot = true
28     @timer.on(:timeout) { tick }
29     @running = false
30     @count = 0
31     @elapsed = 0
32     @time = Qt::Time.new
33   end
34   
35   def stop
36     if @running
37       @elapsed += @time.elapsed
38       @timer.stop
39       
40       @main += @increment
41       
42       @running = false
43       fire :timer => { :main => @main }
44     end
45   end
46   
47   def start
48     if not @running
49       # milliseconds for the next tick
50       delta = [0, (@count + 1) * 100 - @elapsed].max
51       @time.start
52       @timer.start(delta)
53       @running = true
54     end
55   end
56   
57   def tick
58     # update counter
59     @count += 1
60     elapsed = false
61     
62     # update clock state
63     if @count % 10 == 0
64       if @main <= 0
65         # if we get here, there must be
66         # a byoyomi, otherwise the timer would
67         # be stopped
68         @byoyomi.time -= 1
69         if @byoyomi.time <= 0
70           @byoyomi.periods -= 1
71           @byoyomi.time = @byoyomi_time
72           if @byoyomi.periods <= 0
73             elapsed = true
74           end
75         end
76       else
77         @main -= 1
78         if @main <= 0 and (not @byoyomi)
79           elapsed = true
80         end
81       end
82       
83       if elapsed
84         fire :elapsed
85       else
86         fire :timer => timer
87       end
88     end
89     
90     if not elapsed
91       # schedule next tick
92       delta = [0, (@count + 1) * 100 - @elapsed - @time.elapsed].max
93       @timer.start(delta)
94     end
95   end
96   
97   def timer
98     if @main > 0
99       { :main => @main }
100     else
101       { :byoyomi => @byoyomi.dup }
102     end
103   end
104   
105   def set_time(milliseconds)
106     # update time
107     @main = milliseconds / 1000
108     
109     # reset counter
110     @count = 0
111     @elapsed = milliseconds % 1000
112     @time.restart
113     
114     fire :timer => timer
115   end
116   
117   def running?
118     @running
119   end