hlockey 6 → 7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/data/election.json +6 -3
- data/data/infinileague.json +189 -0
- data/data/information.json +2 -1
- data/data/league.json +789 -731
- data/data/messages.json +46 -0
- data/data/previous_election_results.json +32 -39
- data/lib/hlockey/game/fight.rb +78 -29
- data/lib/hlockey/game/weather/chicken.rb +2 -1
- data/lib/hlockey/game/weather/incline.rb +2 -1
- data/lib/hlockey/game/weather/stars.rb +1 -1
- data/lib/hlockey/game/weather/sunset.rb +1 -3
- data/lib/hlockey/game/weather/waves.rb +7 -9
- data/lib/hlockey/game/weather/weatherable.rb +4 -4
- data/lib/hlockey/game.rb +156 -115
- data/lib/hlockey/league.rb +47 -27
- data/lib/hlockey/message.rb +23 -151
- data/lib/hlockey/mod/fencebuilder.rb +1 -2
- data/lib/hlockey/mod/fencedestroyer.rb +1 -1
- data/lib/hlockey/mod/handholding.rb +18 -16
- data/lib/hlockey/mod/immaterial.rb +7 -5
- data/lib/hlockey/mod/locked.rb +9 -3
- data/lib/hlockey/mod/moddable.rb +25 -4
- data/lib/hlockey/mod/powernapper.rb +11 -7
- data/lib/hlockey/mod/punchy.rb +6 -2
- data/lib/hlockey/selfdescribable.rb +1 -3
- data/lib/hlockey/team/player.rb +3 -0
- data/lib/hlockey/team.rb +3 -3
- data/lib/hlockey/utils.rb +36 -4
- data/lib/hlockey/version.rb +1 -1
- data/lib/hlockey.rb +15 -0
- metadata +6 -4
data/lib/hlockey/game.rb
CHANGED
@@ -14,7 +14,7 @@ module Hlockey
|
|
14
14
|
# @return [Team] the teams in the game
|
15
15
|
attr_reader(:home, :away)
|
16
16
|
|
17
|
-
# @return [
|
17
|
+
# @return [Utils::Rng] the RNG the game is getting numbers from
|
18
18
|
attr_reader(:prng)
|
19
19
|
|
20
20
|
# @return [Hash<Symbol => Integer>] the score of each team
|
@@ -31,6 +31,7 @@ module Hlockey
|
|
31
31
|
|
32
32
|
# @return [Boolean] if the game is still in progress
|
33
33
|
attr_reader(:in_progress)
|
34
|
+
alias in_progress? in_progress
|
34
35
|
|
35
36
|
# @return [Integer] what period the game is in
|
36
37
|
attr_reader(:period)
|
@@ -41,26 +42,28 @@ module Hlockey
|
|
41
42
|
# @return [Hash<Player => Hash<Symbol => Number>>] stats of players before changes
|
42
43
|
attr_reader(:pre_tmp_change_stats)
|
43
44
|
|
44
|
-
alias in_progress? in_progress
|
45
|
-
|
46
45
|
# @param home [Team]
|
47
46
|
# @param away [Team]
|
48
|
-
# @param
|
47
|
+
# @param rng_seed [Integer] number to seed the game's RNG with
|
49
48
|
# @param weather [Class<Weatherable>]
|
50
|
-
def initialize(home, away,
|
49
|
+
def initialize(home, away, rng_seed, weather = nil)
|
51
50
|
@home = home
|
52
51
|
@away = away
|
53
|
-
@prng =
|
52
|
+
@prng = Utils::Rng.new(rng_seed)
|
54
53
|
|
55
54
|
@stadium = @home.stadium
|
56
55
|
|
57
|
-
weather
|
56
|
+
if weather.nil?
|
57
|
+
weather = Weather::WEATHERS.sample(random: prng)
|
58
|
+
@rng_seed = @prng.state
|
59
|
+
end
|
60
|
+
|
58
61
|
@weather = weather.new(self)
|
59
62
|
@score = { home: 0, away: 0 }
|
60
63
|
@in_progress = true
|
61
64
|
@actions = 0
|
62
65
|
@period = 1
|
63
|
-
@stream = [Message.
|
66
|
+
@stream = [Message.new(:game_start)]
|
64
67
|
@faceoff = true
|
65
68
|
@faceoff_won = false
|
66
69
|
@team_with_puck = nil
|
@@ -71,55 +74,138 @@ module Hlockey
|
|
71
74
|
@fight_chance = 0
|
72
75
|
@pre_tmp_change_stats = {}
|
73
76
|
@partying_teams = [@home, @away].select { _1.status == :partying }
|
77
|
+
@do_final_cleanup = false
|
74
78
|
|
79
|
+
set_mods_game
|
75
80
|
@weather.on_game_start
|
76
81
|
end
|
77
82
|
|
78
83
|
# @return [Hash]
|
79
|
-
def to_h
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
84
|
+
def to_h(simple: false, stream_start_idx: 0, show_events: true, do_color: false)
|
85
|
+
res = {
|
86
|
+
home: @home.name,
|
87
|
+
away: @away.name,
|
88
|
+
stadium: @stadium.to_s,
|
89
|
+
weather: @weather.to_s,
|
90
|
+
score: @score,
|
91
|
+
in_progress: @in_progress,
|
92
|
+
period: @period
|
93
|
+
}
|
94
|
+
unless simple
|
95
|
+
if show_events
|
96
|
+
res[:events] = @stream[stream_start_idx..]&.map(&:event)
|
97
|
+
else
|
98
|
+
res[:stream] = @stream[stream_start_idx..]&.map { _1.to_s(do_color:) }
|
99
|
+
end
|
100
|
+
|
101
|
+
res[:team_with_puck] = @team_with_puck&.name
|
102
|
+
res[:shooting_chance] = @shooting_chance
|
103
|
+
end
|
104
|
+
|
105
|
+
res
|
106
|
+
end
|
89
107
|
|
90
108
|
# @return [String]
|
91
|
-
def to_s
|
109
|
+
def to_s(do_color: true) =
|
110
|
+
"#{Message.color(@home, do_color:)} vs #{Message.color(@away, do_color:)}"
|
92
111
|
|
93
112
|
# Update the game state by one action
|
94
113
|
def update
|
95
|
-
|
114
|
+
if @do_final_cleanup
|
115
|
+
@in_progress = false
|
116
|
+
set_mods_game(unset: true)
|
117
|
+
return
|
118
|
+
end
|
96
119
|
|
97
120
|
do_action
|
98
121
|
handle_parties
|
99
122
|
end
|
100
123
|
|
124
|
+
# Makes a player pass in the game, regardless of if it would normally occur
|
125
|
+
def pass
|
126
|
+
sender = puck_holder
|
127
|
+
receiver_pos = pass_reciever_pos
|
128
|
+
interceptor_pos = defensive_pos
|
129
|
+
|
130
|
+
@stream << Message.new(:game_pass,
|
131
|
+
sender:,
|
132
|
+
receiver: @team_with_puck.roster[receiver_pos])
|
133
|
+
|
134
|
+
if !@faceoff && try_take_puck(interceptor_pos, dis: 10 - @shooting_chance)
|
135
|
+
@stream << Message.new(:game_pass_intercept,
|
136
|
+
sender:,
|
137
|
+
receiver: @puckless_team.roster[receiver_pos],
|
138
|
+
interceptor: @team_with_puck.roster[interceptor_pos])
|
139
|
+
return
|
140
|
+
end
|
141
|
+
|
142
|
+
@puck_holder_pos = receiver_pos
|
143
|
+
@shooting_chance += 1
|
144
|
+
end
|
145
|
+
|
146
|
+
# Makes a player check in the game, regardless of if it would normally occur
|
147
|
+
def check
|
148
|
+
defender_pos = defensive_pos
|
149
|
+
defender = @puckless_team.roster[defender_pos]
|
150
|
+
|
151
|
+
@stream << Message.new(:game_hit, defender:, puck_holder:)
|
152
|
+
if !puck_holder.mods_do(:on_got_hit, defender) &&
|
153
|
+
try_take_puck(defender_pos, stat: :defense)
|
154
|
+
@stream << Message.new(:game_possession_change,
|
155
|
+
player: defender, new_puck_team: defender.team)
|
156
|
+
end
|
157
|
+
|
158
|
+
@fight_chance += 0.1
|
159
|
+
end
|
160
|
+
|
161
|
+
# Makes a player shoot in the game, regardless of if it would normally occur
|
162
|
+
# @param audacity [Boolean] if the function was called by Audacity weather
|
163
|
+
def shoot(audacity: false)
|
164
|
+
return if @puck_holder_pos.nil?
|
165
|
+
|
166
|
+
shooter = puck_holder
|
167
|
+
|
168
|
+
@stream << Message.new(audacity ? :game_shot_audacious : :game_shot, shooter:)
|
169
|
+
|
170
|
+
blocking_pos = @prng.rand(2).zero? ? :ldef : :rdef
|
171
|
+
return if try_block_shot(blocking_pos) || try_block_shot(:goalie)
|
172
|
+
|
173
|
+
# Goal scored
|
174
|
+
|
175
|
+
scoring_team = @team_with_puck == @home ? :home : :away
|
176
|
+
@score[scoring_team] += 1
|
177
|
+
weather.on_goal(scoring_team)
|
178
|
+
|
179
|
+
@stream << Message.new(:game_shot_scored, shooter:)
|
180
|
+
|
181
|
+
start_fight if @prng.rand(3 + @fight_chance) > 3
|
182
|
+
|
183
|
+
start_faceoff
|
184
|
+
@actions = ACTIONS_PER_PERIOD - 1 if @period > NON_OT_PERIODS # Sudden death OT
|
185
|
+
end
|
186
|
+
|
101
187
|
# Starts a fight in the game
|
102
188
|
# @param starting_player [Player, nil] the player that started the fight (optional)
|
103
189
|
def start_fight(starting_player = nil)
|
104
190
|
@fight = Fight.new(self, starting_player)
|
105
|
-
@stream << Message.
|
106
|
-
|
191
|
+
@stream << Message.new(:fight_start,
|
192
|
+
home_player: @fight.players[:home].first,
|
193
|
+
away_player: @fight.players[:away].first)
|
107
194
|
end
|
108
195
|
|
109
196
|
private
|
110
197
|
|
111
198
|
# Does an action in the game
|
112
199
|
def do_action
|
113
|
-
@stream << Message.
|
200
|
+
@stream << Message.new(:game_period_start, period: @period) if @actions.zero?
|
114
201
|
@actions += 1
|
115
202
|
|
116
203
|
@weather.on_action
|
117
|
-
(@home.roster.values + @away.roster.values).each { _1.mods_do(:on_action
|
204
|
+
(@home.roster.values + @away.roster.values).each { _1.mods_do(:on_action) }
|
118
205
|
|
119
206
|
unless @fight.nil?
|
120
|
-
|
121
|
-
@stream
|
122
|
-
return unless fight_message.event == :FightEnded
|
207
|
+
@fight.next_action
|
208
|
+
return unless @stream.last.event == :fight_end
|
123
209
|
|
124
210
|
morale_change_amount = (@fight.score[:home] - @fight.score[:away]) / 10.0
|
125
211
|
change_morale(@home, morale_change_amount)
|
@@ -156,92 +242,30 @@ module Hlockey
|
|
156
242
|
player = team.roster.values.sample(random: @prng)
|
157
243
|
stat = player.stats.keys.sample(random: @prng)
|
158
244
|
player.stats[stat] += 0.1
|
159
|
-
@stream << Message.
|
245
|
+
@stream << Message.new(:game_partying, player:)
|
160
246
|
next if @pre_tmp_change_stats[player].nil?
|
161
247
|
|
162
248
|
@pre_tmp_change_stats[player][stat] += 0.1
|
163
249
|
end
|
164
250
|
end
|
165
251
|
|
166
|
-
# Makes a player pass in the game, regardless of if it would normally occur
|
167
|
-
def pass
|
168
|
-
sender = puck_holder
|
169
|
-
receiver = pass_reciever
|
170
|
-
interceptor = defensive_pos
|
171
|
-
|
172
|
-
if !@faceoff && try_take_puck(interceptor, 7 - @shooting_chance, :agility)
|
173
|
-
@stream << Message.Pass(sender,
|
174
|
-
@puckless_team.roster[receiver],
|
175
|
-
@shooting_chance,
|
176
|
-
@team_with_puck.roster[interceptor],
|
177
|
-
@team_with_puck)
|
178
|
-
return
|
179
|
-
end
|
180
|
-
|
181
|
-
@puck_holder_pos = receiver
|
182
|
-
@shooting_chance += 1
|
183
|
-
@stream << Message.Pass(sender, @team_with_puck.roster[receiver], @shooting_chance)
|
184
|
-
end
|
185
|
-
|
186
|
-
# Makes a player check in the game, regardless of if it would normally occur
|
187
|
-
def check
|
188
|
-
defender_pos = defensive_pos
|
189
|
-
defender = @puckless_team.roster[defender_pos]
|
190
|
-
|
191
|
-
mods_messages = puck_holder.mods.map { _1.on_got_hit(self, defender) }.compact
|
192
|
-
|
193
|
-
@stream << Message.Hit(puck_holder,
|
194
|
-
defender,
|
195
|
-
mods_messages.empty? && try_take_puck(defender_pos),
|
196
|
-
@team_with_puck,
|
197
|
-
@shooting_chance)
|
198
|
-
@stream.concat(mods_messages)
|
199
|
-
@fight_chance += 0.1
|
200
|
-
end
|
201
|
-
|
202
|
-
# Makes a player shoot in the game, regardless of if it would normally occur
|
203
|
-
# This is called outside of the class by Audacity weather (using #send)
|
204
|
-
# @param audacity [Boolean] if the function was called by Audacity weather
|
205
|
-
def shoot(audacity: false)
|
206
|
-
return if @puck_holder_pos.nil?
|
207
|
-
|
208
|
-
if @shooting_chance < 5 &&
|
209
|
-
try_block_shot(@prng.rand(2).zero? ? :ldef : :rdef, audacity:)
|
210
|
-
return
|
211
|
-
end
|
212
|
-
return if try_block_shot(:goalie, audacity:)
|
213
|
-
|
214
|
-
# Goal scored
|
215
|
-
|
216
|
-
scoring_team = @team_with_puck == @home ? :home : :away
|
217
|
-
@score[scoring_team] += 1
|
218
|
-
weather.on_goal(scoring_team)
|
219
|
-
|
220
|
-
@stream << Message.ShootScore(puck_holder, @home, @away, *@score.values, audacity)
|
221
|
-
|
222
|
-
start_fight if @prng.rand(3 + @fight_chance) > 3
|
223
|
-
|
224
|
-
start_faceoff
|
225
|
-
@actions = ACTIONS_PER_PERIOD - 1 if @period > NON_OT_PERIODS # Sudden death OT
|
226
|
-
end
|
227
|
-
|
228
252
|
def end_period
|
229
253
|
@weather.on_period_end
|
230
|
-
@stream << Message.
|
254
|
+
@stream << Message.new(:game_period_end, period: @period)
|
231
255
|
@actions = 0
|
232
256
|
@period += 1
|
233
257
|
start_faceoff
|
234
258
|
|
235
259
|
if @period > NON_OT_PERIODS &&
|
236
|
-
|
260
|
+
(@score[:home] != @score[:away] || @period > TOTAL_PERIODS)
|
237
261
|
# Game is over
|
238
262
|
@pre_tmp_change_stats.each { |player, stats| player.stats = stats }
|
239
263
|
@weather.on_game_end
|
240
|
-
@in_progress = false
|
241
264
|
winner, loser = @score[:away] > @score[:home] ? [@away, @home] : [@home, @away]
|
242
|
-
@stream << Message.
|
265
|
+
@stream << Message.new(:game_end, winning_team: winner)
|
243
266
|
winner.wins += 1
|
244
267
|
loser.losses += 1
|
268
|
+
@do_final_cleanup = true
|
245
269
|
end
|
246
270
|
end
|
247
271
|
|
@@ -271,7 +295,7 @@ module Hlockey
|
|
271
295
|
@shooting_chance = 2
|
272
296
|
@puck_holder_pos = :center
|
273
297
|
@faceoff_won = true
|
274
|
-
@stream << Message.
|
298
|
+
@stream << Message.new(:game_faceoff, winning_player: puck_holder)
|
275
299
|
end
|
276
300
|
|
277
301
|
def switch_team_with_puck(team_with_puck = @team_with_puck)
|
@@ -288,7 +312,7 @@ module Hlockey
|
|
288
312
|
# @param dis [Integer] disadvantage against taking puck
|
289
313
|
# @param stat [Symbol] stat compared to change the odds of success/failure
|
290
314
|
# @return [Boolean] if taking the puck succeeded
|
291
|
-
def try_take_puck(pos, dis
|
315
|
+
def try_take_puck(pos, dis: 0, stat: :agility)
|
292
316
|
return false unless action_succeeds?(@puckless_team.roster[pos].stats[stat] - dis,
|
293
317
|
puck_holder.stats[stat])
|
294
318
|
|
@@ -299,20 +323,38 @@ module Hlockey
|
|
299
323
|
end
|
300
324
|
|
301
325
|
# @return [Boolean] if blocking the shot succeeded
|
302
|
-
def try_block_shot(pos
|
326
|
+
def try_block_shot(pos)
|
303
327
|
blocker = @puckless_team.roster[pos]
|
304
328
|
|
305
|
-
|
306
|
-
|
329
|
+
defense = if pos == :goalie
|
330
|
+
blocker.stats[:defense] * 2
|
331
|
+
else
|
332
|
+
blocker.stats[:defense] - @shooting_chance
|
333
|
+
end
|
334
|
+
return false unless action_succeeds?(defense, puck_holder.stats[:offense])
|
307
335
|
|
308
336
|
@shooting_chance += 1
|
309
|
-
@stream << Message.
|
310
|
-
|
311
|
-
|
337
|
+
@stream << Message.new(:game_shot_blocked, blocker:)
|
338
|
+
|
339
|
+
if action_succeeds?(defense, 0)
|
340
|
+
# blocker takes puck
|
341
|
+
switch_team_with_puck
|
342
|
+
@puck_holder_pos = pos
|
343
|
+
else
|
344
|
+
@puck_holder_pos = Utils.weighted_random(weights(true), @prng)
|
345
|
+
try_take_puck(Utils.weighted_random(weights(false).tap { _1.delete(pos) }, @prng))
|
346
|
+
end
|
347
|
+
|
348
|
+
@stream << Message.new(:game_possession_change,
|
349
|
+
player: puck_holder, new_puck_team: @team_with_puck)
|
312
350
|
|
313
351
|
true
|
314
352
|
end
|
315
353
|
|
354
|
+
# @param unset [Boolean] if the game should be set to nil (done when game is over)
|
355
|
+
def set_mods_game(unset: false) =
|
356
|
+
(@home.players + @away.players).each { _1.mods_do(:game=, unset ? nil : self) }
|
357
|
+
|
316
358
|
# Add amount to each stat of each player of team until the end of the game
|
317
359
|
# @param team [Team]
|
318
360
|
# @param amount [Numeric]
|
@@ -320,21 +362,22 @@ module Hlockey
|
|
320
362
|
return if amount.zero?
|
321
363
|
|
322
364
|
team.roster.each_value do |player|
|
323
|
-
|
324
|
-
|
325
|
-
end
|
326
|
-
|
327
|
-
player.stats.transform_values! { |stat| stat + amount }
|
365
|
+
@pre_tmp_change_stats[player] ||= player.stats.clone
|
366
|
+
player.stats.transform_values! { _1 + amount }
|
328
367
|
end
|
329
368
|
|
330
|
-
@stream <<
|
369
|
+
@stream << if amount.positive?
|
370
|
+
Message.new(:fight_end_morale_gain, team:, num: amount)
|
371
|
+
else
|
372
|
+
Message.new(:fight_end_morale_loss, team:, num: -amount)
|
373
|
+
end
|
331
374
|
end
|
332
375
|
|
333
|
-
# @return [Player]
|
376
|
+
# @return [Team::Player]
|
334
377
|
def puck_holder = @team_with_puck.roster[@puck_holder_pos]
|
335
378
|
|
336
379
|
# @return [Symbol]
|
337
|
-
def
|
380
|
+
def pass_reciever_pos
|
338
381
|
w = weights(@shooting_chance > 3)
|
339
382
|
w.delete(@puck_holder_pos)
|
340
383
|
Utils.weighted_random(w, @prng)
|
@@ -345,11 +388,9 @@ module Hlockey
|
|
345
388
|
|
346
389
|
# @return [Hash<Symbol => Integer>]
|
347
390
|
def weights(offensive)
|
348
|
-
if offensive
|
349
|
-
|
350
|
-
|
351
|
-
{ lwing: 1, center: 1, rwing: 1, ldef: 3, rdef: 3 }
|
352
|
-
end
|
391
|
+
return { lwing: 2, center: 2, rwing: 2, ldef: 1, rdef: 1 } if offensive
|
392
|
+
|
393
|
+
{ lwing: 1, center: 1, rwing: 1, ldef: 3, rdef: 3 }
|
353
394
|
end
|
354
395
|
end
|
355
396
|
end
|
data/lib/hlockey/league.rb
CHANGED
@@ -11,6 +11,10 @@ module Hlockey
|
|
11
11
|
class League
|
12
12
|
GAMES_IN_REGULAR_SEASON = 111
|
13
13
|
|
14
|
+
# @return [Boolean]
|
15
|
+
attr_reader(:infinite)
|
16
|
+
alias infinite? infinite
|
17
|
+
|
14
18
|
# @return [Time]
|
15
19
|
attr_reader(:start_time)
|
16
20
|
|
@@ -20,6 +24,9 @@ module Hlockey
|
|
20
24
|
# @return [Integer]
|
21
25
|
attr_reader(:day)
|
22
26
|
|
27
|
+
# @return [String]
|
28
|
+
attr_reader(:season)
|
29
|
+
|
23
30
|
# @return [Array<Game>]
|
24
31
|
attr_reader(:games, :games_in_progress)
|
25
32
|
|
@@ -32,21 +39,16 @@ module Hlockey
|
|
32
39
|
# @return [Array<Team>]
|
33
40
|
attr_reader(:teams, :playoff_teams)
|
34
41
|
|
35
|
-
# @param start_time [Array<Integer
|
36
|
-
# @param divisions [Hash<Symbol => Array<Hash
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
divisions ||= league_hash[:divisions]
|
42
|
-
end
|
43
|
-
|
44
|
-
@start_time = Time.utc(*start_time)
|
45
|
-
@start_time.localtime
|
46
|
-
|
42
|
+
# @param start_time [Array<Integer>] parameters passed to Time.new
|
43
|
+
# @param divisions [Hash<Symbol => Array<Hash>>] division data
|
44
|
+
# @param infinite [Boolean] if the league is infinite, rather than a single season
|
45
|
+
def initialize(infinite:, start_time:, divisions:)
|
46
|
+
@infinite = infinite
|
47
|
+
@start_time = Time.utc(*start_time).localtime
|
47
48
|
@divisions = divisions.transform_values { |teams| teams.map { Team.new(**_1) } }
|
48
49
|
|
49
50
|
@day = 0
|
51
|
+
@season = infinite ? "?" : VERSION
|
50
52
|
|
51
53
|
@games = []
|
52
54
|
@games_in_progress = []
|
@@ -57,24 +59,27 @@ module Hlockey
|
|
57
59
|
@last_update_time = @start_time
|
58
60
|
@passed_updates = 0
|
59
61
|
|
60
|
-
@prng =
|
62
|
+
@prng = Utils::Rng.new(@start_time.to_i)
|
61
63
|
|
62
64
|
@teams = @divisions.values.flatten
|
63
65
|
@sorted_teams_by_wins = @teams.shuffle(random: @prng)
|
64
66
|
@playoff_teams = []
|
65
67
|
|
68
|
+
@game_in_matchup = @matchup_game_amt = 3
|
69
|
+
|
70
|
+
return if @infinite
|
71
|
+
|
66
72
|
# warm vs warm / cool vs cool
|
67
73
|
rotated_divisions = @divisions.values.rotate
|
68
74
|
@shuffled_division_pairs = Array.new(2) do |i|
|
69
75
|
(rotated_divisions[i] + rotated_divisions[-i - 1]).shuffle(random: @prng)
|
70
76
|
end
|
71
|
-
|
72
|
-
@game_in_matchup = @matchup_game_amt = 3
|
73
77
|
end
|
74
78
|
|
75
79
|
# @return [Hash]
|
76
80
|
def to_h(simple: false)
|
77
81
|
res = {
|
82
|
+
infinite: @infinite,
|
78
83
|
start_time: @start_time.getutc.to_a.first(6).reverse,
|
79
84
|
divisions: @divisions.transform_values { |d| d.map { |t| t.to_h(simple:) } }
|
80
85
|
}
|
@@ -107,13 +112,16 @@ module Hlockey
|
|
107
112
|
return unless intervals.positive?
|
108
113
|
|
109
114
|
intervals.times do |i|
|
110
|
-
if ((i + @passed_updates) % UPDATES_PER_HOUR).zero?
|
115
|
+
if !@infinite && ((i + @passed_updates) % UPDATES_PER_HOUR).zero?
|
111
116
|
update_teams
|
112
117
|
new_games
|
113
|
-
|
114
|
-
update_games
|
118
|
+
next
|
115
119
|
end
|
116
120
|
|
121
|
+
update_games
|
122
|
+
|
123
|
+
new_games if @infinite && @games_in_progress.empty?
|
124
|
+
|
117
125
|
break if @champion_team
|
118
126
|
end
|
119
127
|
|
@@ -161,7 +169,9 @@ module Hlockey
|
|
161
169
|
def new_games
|
162
170
|
if @game_in_matchup != @matchup_game_amt
|
163
171
|
# New game in matchups
|
164
|
-
@games
|
172
|
+
prev_games = @games
|
173
|
+
@games = []
|
174
|
+
prev_games.each { new_game(_1.away, _1.home) }
|
165
175
|
@game_in_matchup += 1
|
166
176
|
return
|
167
177
|
end
|
@@ -171,12 +181,17 @@ module Hlockey
|
|
171
181
|
@game_in_matchup = 1
|
172
182
|
@day += 1
|
173
183
|
|
184
|
+
if @infinite
|
185
|
+
new_matchups(@teams)
|
186
|
+
return
|
187
|
+
end
|
188
|
+
|
174
189
|
case @day
|
175
190
|
when 1..27
|
176
191
|
@shuffled_division_pairs.each { new_matchups(_1, reverse: @day.even?) }
|
177
192
|
when 28..37
|
178
193
|
@shuffled_division_pairs.transpose.each_with_index do |pair, i|
|
179
|
-
|
194
|
+
new_game(*pair, reverse: i.even?)
|
180
195
|
end
|
181
196
|
@shuffled_division_pairs.first.rotate!
|
182
197
|
when 38
|
@@ -192,13 +207,17 @@ module Hlockey
|
|
192
207
|
new_playoff_matchups
|
193
208
|
when 41
|
194
209
|
@champion_team = @playoff_teams.find(&:positive_record?)
|
195
|
-
@alerts << Message.
|
210
|
+
@alerts << Message.new(:season_champion, season: @season, team: @champion_team)
|
196
211
|
end
|
197
212
|
end
|
198
213
|
|
199
214
|
def update_games
|
200
215
|
@games_in_progress.each(&:update)
|
201
|
-
|
216
|
+
|
217
|
+
prev_in_progress_amt = @games_in_progress.length
|
218
|
+
@games_in_progress.select!(&:in_progress?)
|
219
|
+
return if @games_in_progress.length == prev_in_progress_amt
|
220
|
+
|
202
221
|
@divisions.transform_values!(&method(:sort_teams_by_wins))
|
203
222
|
end
|
204
223
|
|
@@ -208,12 +227,12 @@ module Hlockey
|
|
208
227
|
new_matchups(@playoff_teams, rotate: false)
|
209
228
|
end
|
210
229
|
|
211
|
-
# @param matchup_teams[Array<Team>]
|
230
|
+
# @param matchup_teams [Array<Team>]
|
212
231
|
# @param rotate [Boolean]
|
213
232
|
# @param reverse [Boolean]
|
214
233
|
def new_matchups(matchup_teams, rotate: true, reverse: false)
|
215
234
|
(matchup_teams.length / 2).times do |i|
|
216
|
-
|
235
|
+
new_game(matchup_teams[i], matchup_teams[-i - 1], reverse:)
|
217
236
|
end
|
218
237
|
matchup_teams.insert(1, matchup_teams.pop) if rotate
|
219
238
|
end
|
@@ -222,10 +241,11 @@ module Hlockey
|
|
222
241
|
# @param away [Team]
|
223
242
|
# @param reverse [Boolean]
|
224
243
|
def new_game(home, away, reverse: false)
|
225
|
-
teams = [home, away]
|
226
|
-
teams.reverse! if reverse
|
244
|
+
teams = reverse ? [away, home] : [home, away]
|
227
245
|
|
228
|
-
Game.new(*teams, @prng)
|
246
|
+
game = Game.new(*teams, @prng.rand)
|
247
|
+
@games << game
|
248
|
+
@games_in_progress << game
|
229
249
|
end
|
230
250
|
|
231
251
|
# @return [Array<Team>]
|