forked from exavideo/scoreboard
-
Notifications
You must be signed in to change notification settings - Fork 1
/
game_clock.rb
217 lines (191 loc) · 5.4 KB
/
game_clock.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
# Copyright 2011, 2014 Exavideo LLC.
#
# This file is part of Exaboard.
#
# Exaboard is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Exaboard is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Exaboard. If not, see <http://www.gnu.org/licenses/>.
##
# A class representing the game clock.
#
# The time is stored with resolution to the nearest 1/10 second; this is the
# resolution of most scoreboard sync feeds we deal with. The clock also keeps
# track of clock-related game rules, such as period and overtime lengths and
# the number of periods in the game.
#
# Times are always internally represented in 1/10 second increments. Depending
# on context, this is either time from the beginning of the game, or time
# remaining in the current period.
#
# The time is stored internally as a time from the beginning of the game. This
# is a useful form for penalty queue calculations. However, this means that
# changing the clock settings may change this value.
class GameClock
##
# Object representing clock-related game rules. Keeps track of period
# length and number, as well as overtime length. Immutable once created.
class Settings
def initialize(period_length, overtime_length, num_periods, num_overtimes)
@period_length = period_length
@num_periods = num_periods
@overtime_length = overtime_length
@num_overtimes = num_overtimes
end
attr_reader :period_length
attr_reader :overtime_length
attr_reader :num_periods
attr_reader :num_overtimes
end
##
# Initializes the game clock.
def initialize
# Clock value, in tenths of seconds
@value = 0
@last_start = nil
@period = 1
# load defaults for hockey, these can be changed using load_settings
@period_length = 20*60*10
@overtime_length = 5*60*10
@num_periods = 3
@num_overtimes = 1
end
##
# Returns the time at the end of the current period,
# relative to the start of the game.
def period_end
if @period <= @num_periods
@period_length * @period
else
@period_length * @num_periods + @overtime_length * (@period - @num_periods)
end
end
##
# Load new settings into the clock. This maintains the current period,
# and the time remaining in the period. However, the tim
def load_settings(preset)
current_time_remaining = period_remaining
STDERR.puts "current_time_remaining #{current_time_remaining}"
@period_length = preset.period_length
@overtime_length = preset.overtime_length
@num_periods = preset.num_periods
@num_overtimes = preset.num_overtimes
self.period_remaining = current_time_remaining
end
##
# Return the total time elapsed from the beginning of the game. This time
# may jump forwards or backwards if the clock is adjusted or if the clock
# settings have changed.
def time_elapsed
if @last_start
elapsed = Time.now - @last_start
# compute the elapsed time in tenths of seconds
value_now = @value + (elapsed * 10).to_i
# we won't go past the end of a period without an explicit restart
if value_now > period_end
value_now = period_end
@value = value_now
@last_start = nil
end
value_now
else
@value
end
end
##
# Advance the clock from the end of the period to the start of the
# subsequent period.
def period_advance
pl = @period_length
if @period+1 > @num_periods
pl = @overtime_length
end
reset_time(pl, @period+1)
end
##
# Reset the time, given the current period and the time
# remaining in that period.
def reset_time(remaining, newperiod)
if newperiod <= @num_periods
# normal period
@value = @period_length - remaining + @period_length*(newperiod-1)
else
# overtime
@value = @overtime_length*(newperiod-@num_periods) - remaining + @period_length*@num_periods
end
if @last_start != nil
@last_start = Time.now
end
@period = newperiod
end
attr_reader :period
attr_reader :num_periods
attr_reader :overtime_length
attr_reader :num_overtimes
##
# Start running the clock.
def start
if @value == @period_end
period_advance
end
if @last_start == nil
@last_start = Time.now
end
end
##
# Stop running the clock.
def stop
@value = time_elapsed
@last_start = nil
end
##
# Return true if the clock is currently running.
def running?
if @last_start
true
else
false
end
end
##
# Set the amount of time remaining in the current period.
def period_remaining=(tenths)
@value = period_end - tenths
if running?
@last_start = Time.now
end
end
##
# Return the amount of time remaining in the current period.
def period_remaining
period_end - time_elapsed
end
##
# Convert a string representation of time to a numeric value
# in 1/10 second increments.
def self.parse_clock(time_str)
if time_str =~ /^((\d+)?:)?([0-5]\d(\.\d)?)$/
seconds = $3.to_f
minutes = $2.to_i
minutes * 600 + (seconds * 10).to_i
else
fail "invalid clock value"
end
end
def to_json(*args)
{
'running' => running?,
'period_remaining' => period_remaining,
'period' => period,
'time_elapsed' => time_elapsed,
}.to_json
end
end