hlockey 6 → 7
Sign up to get free protection for your applications and to get access to all the features.
- 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>]
|