hlockey 1 → 3
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.yaml +16 -0
- data/data/external/names.txt +19948 -0
- data/data/information.yaml +12 -0
- data/data/league.yaml +846 -0
- data/data/links.yaml +2 -0
- data/lib/hlockey/constants.rb +4 -0
- data/lib/hlockey/data.rb +17 -0
- data/lib/hlockey/game/actions.rb +30 -0
- data/lib/hlockey/game/fight.rb +83 -0
- data/lib/hlockey/game.rb +254 -0
- data/lib/hlockey/league.rb +150 -0
- data/lib/hlockey/message.rb +133 -0
- data/lib/hlockey/player.rb +63 -0
- data/lib/hlockey/team.rb +59 -0
- data/lib/hlockey/utils.rb +12 -0
- data/lib/hlockey/version.rb +3 -0
- data/lib/hlockey/weather.rb +114 -0
- data/lib/hlockey.rb +1 -0
- metadata +26 -15
- data/bin/hlockey +0 -90
- data/lib/data/divisions.yaml +0 -845
- data/lib/data/season.rb +0 -27
- data/lib/game.rb +0 -158
- data/lib/league.rb +0 -140
- data/lib/messages.rb +0 -66
data/lib/data/season.rb
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Season
|
4
|
-
NUMBER = 1
|
5
|
-
START_TIME = Time.utc(2022, 7, 4, 17).localtime.freeze
|
6
|
-
ELECTION = {
|
7
|
-
'Bribery': {
|
8
|
-
'Eat More RAM': 'Add an online interface to the league.',
|
9
|
-
'Weather': 'Move all the stadiums outdoors.',
|
10
|
-
'We Do A Little Losing': 'Recognize the losingest teams with an Underbracket.'
|
11
|
-
},
|
12
|
-
'Treasure': {
|
13
|
-
'Vengence': 'All your team\'s stats are boosted by your team\'s losses * 0.005.',
|
14
|
-
'Dave': 'The worst stat on your team is set to 4.5.',
|
15
|
-
'Nice': 'Boost your worst player by 0.69 in every stat.',
|
16
|
-
'Convenient Math Error': 'Your team starts the next season with 5 wins.'
|
17
|
-
},
|
18
|
-
'Coaching': {
|
19
|
-
'Player Swapping': 'Swap the positions of 2 players on your team.',
|
20
|
-
'Stat Swapping': 'Swap 2 stats of a player on your team.',
|
21
|
-
'Draft': 'Replace a player with a random new player.',
|
22
|
-
'Small Gamble': 'All your team\'s stats go up or down by 0.5 at random.',
|
23
|
-
'Big Gamble': 'All your team\'s stats go up or down by 1.0 at random.'
|
24
|
-
}
|
25
|
-
}.freeze
|
26
|
-
ELECTION_FORM = 'https://forms.gle/saLp3ucxg2ERsY9L7'
|
27
|
-
end
|
data/lib/game.rb
DELETED
@@ -1,158 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative('./messages')
|
4
|
-
|
5
|
-
class Game
|
6
|
-
attr_reader(:home, :away, :stream, :in_progress)
|
7
|
-
|
8
|
-
def initialize(home, away, prng)
|
9
|
-
@home = home
|
10
|
-
@away = away
|
11
|
-
@prng = prng
|
12
|
-
@stream = [Messages.StartOfGame(to_s)]
|
13
|
-
@in_progress = true
|
14
|
-
@score = { home: 0, away: 0 }
|
15
|
-
@actions = 0
|
16
|
-
@period = 1
|
17
|
-
@face_off = true
|
18
|
-
@team_with_puck = nil
|
19
|
-
@puckless_team = nil
|
20
|
-
@puck_holder = nil
|
21
|
-
@shooting_chance = 0
|
22
|
-
end
|
23
|
-
|
24
|
-
def to_s
|
25
|
-
"#{@home.emoji} #{@home.name} vs #{@away.name} #{@away.emoji}"
|
26
|
-
end
|
27
|
-
|
28
|
-
def update
|
29
|
-
return unless @in_progress
|
30
|
-
|
31
|
-
@stream << Messages.StartOfPeriod(@period) if @actions.zero?
|
32
|
-
@actions += 1
|
33
|
-
|
34
|
-
if @actions == 60
|
35
|
-
@stream << Messages.EndOfPeriod(@period, @home.name, @away.name, *@score.values)
|
36
|
-
@actions = 0
|
37
|
-
@period += 1
|
38
|
-
start_faceoff
|
39
|
-
|
40
|
-
if @period > 3 && !(@score[:home] == @score[:away] && @period < 12)
|
41
|
-
# Game is over
|
42
|
-
@in_progress = false
|
43
|
-
|
44
|
-
winner, loser = @score[:away] > @score[:home] ? [@away, @home] : [@home, @away]
|
45
|
-
|
46
|
-
@stream << Messages.EndOfGame(winner.name)
|
47
|
-
winner.wins += 1
|
48
|
-
loser.losses += 1
|
49
|
-
end
|
50
|
-
return
|
51
|
-
end
|
52
|
-
|
53
|
-
if @face_off
|
54
|
-
if @puck_holder.nil?
|
55
|
-
# Pass opposite team to who wins the puck to switch_team_with_puck,
|
56
|
-
# so @team_with_puck & @puckless_team are set to the correct values.
|
57
|
-
switch_team_with_puck(if action_succeeds?(@home.roster[:center].stats[:offense],
|
58
|
-
@away.roster[:center].stats[:offense])
|
59
|
-
@away
|
60
|
-
else
|
61
|
-
@home
|
62
|
-
end)
|
63
|
-
|
64
|
-
@puck_holder = @team_with_puck.roster[:center]
|
65
|
-
@stream << Messages.FaceOff(@puck_holder)
|
66
|
-
else
|
67
|
-
pass
|
68
|
-
@face_off = false
|
69
|
-
end
|
70
|
-
return
|
71
|
-
end
|
72
|
-
|
73
|
-
case @prng.rand 5 + @shooting_chance
|
74
|
-
when 0..4
|
75
|
-
pass
|
76
|
-
when 5..6 # Check
|
77
|
-
defender = puckless_non_goalie
|
78
|
-
@stream << Messages.Hit(@puck_holder, defender,
|
79
|
-
try_take_puck(defender, 0, :defense))
|
80
|
-
else # Shoot
|
81
|
-
unless @shooting_chance < 5 &&
|
82
|
-
try_block_shot(@puckless_team.roster[@prng.rand(2).zero? ? :ldef : :rdef]) ||
|
83
|
-
try_block_shot(@puckless_team.roster[:goalie])
|
84
|
-
@score[@team_with_puck == @home ? :home : :away] += 1
|
85
|
-
|
86
|
-
@stream << Messages.Shoot(@puck_holder,
|
87
|
-
nil, false,
|
88
|
-
@home.name, @away.name,
|
89
|
-
*@score.values)
|
90
|
-
|
91
|
-
start_faceoff
|
92
|
-
@actions = 59 if @period > 3 # Sudden death overtime
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
private
|
98
|
-
|
99
|
-
def action_succeeds?(helping_stat, hindering_stat)
|
100
|
-
@prng.rand(10) + helping_stat - hindering_stat > 4
|
101
|
-
end
|
102
|
-
|
103
|
-
def start_faceoff
|
104
|
-
@shooting_chance = 0
|
105
|
-
@face_off = true
|
106
|
-
@puck_holder = nil
|
107
|
-
end
|
108
|
-
|
109
|
-
def switch_team_with_puck(team_with_puck = @team_with_puck)
|
110
|
-
@team_with_puck, @puckless_team = if team_with_puck == @home
|
111
|
-
[@away, @home]
|
112
|
-
else
|
113
|
-
[@home, @away]
|
114
|
-
end
|
115
|
-
@shooting_chance = 0
|
116
|
-
end
|
117
|
-
|
118
|
-
def pass
|
119
|
-
sender = @puck_holder
|
120
|
-
receiver = puckless_non_goalie(@team_with_puck)
|
121
|
-
interceptor = puckless_non_goalie
|
122
|
-
|
123
|
-
if !@face_off && try_take_puck(interceptor, 4) # Pass intercepted
|
124
|
-
@stream << Messages.Pass(sender, receiver, interceptor)
|
125
|
-
return
|
126
|
-
end
|
127
|
-
|
128
|
-
@stream << Messages.Pass(sender, receiver)
|
129
|
-
@puck_holder = receiver
|
130
|
-
@shooting_chance += 1
|
131
|
-
end
|
132
|
-
|
133
|
-
def try_take_puck(player, dis = 0, stat = :agility)
|
134
|
-
return false unless action_succeeds?(player.stats[stat] - dis,
|
135
|
-
@puck_holder.stats[stat])
|
136
|
-
|
137
|
-
switch_team_with_puck
|
138
|
-
@puck_holder = player
|
139
|
-
|
140
|
-
true
|
141
|
-
end
|
142
|
-
|
143
|
-
def try_block_shot(blocker)
|
144
|
-
return false unless action_succeeds?(blocker.stats[:defense],
|
145
|
-
@puck_holder.stats[:offense])
|
146
|
-
|
147
|
-
@shooting_chance += 1
|
148
|
-
@stream << Messages.Shoot(@puck_holder, blocker, try_take_puck(blocker))
|
149
|
-
|
150
|
-
true
|
151
|
-
end
|
152
|
-
|
153
|
-
def puckless_non_goalie(team = @puckless_team)
|
154
|
-
team.roster.values.select do |p|
|
155
|
-
p != team.roster[:goalie] and p != @puck_holder
|
156
|
-
end.sample(random: @prng)
|
157
|
-
end
|
158
|
-
end
|
data/lib/league.rb
DELETED
@@ -1,140 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require('yaml')
|
4
|
-
require_relative('./game')
|
5
|
-
|
6
|
-
class League
|
7
|
-
attr_reader(:divisions, :day, :games_in_progress, :playoff_teams, :champion_team)
|
8
|
-
|
9
|
-
class Team
|
10
|
-
private_class_method(:new)
|
11
|
-
|
12
|
-
attr_accessor(:wins, :losses)
|
13
|
-
attr_reader(:name, :emoji, :roster)
|
14
|
-
|
15
|
-
def to_s
|
16
|
-
"#{@emoji} #{@name}"
|
17
|
-
end
|
18
|
-
|
19
|
-
def print_win_loss
|
20
|
-
puts(" #{to_s.ljust 26} #{@wins}-#{@losses}")
|
21
|
-
end
|
22
|
-
|
23
|
-
def print_roster
|
24
|
-
puts(to_s)
|
25
|
-
@roster.each do |pos, player|
|
26
|
-
puts(" #{pos.to_s.ljust(6)}: #{player}")
|
27
|
-
player.stats.each { |stat, value| puts(" #{stat}: #{value.round 1}") }
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def self.sort(teams)
|
32
|
-
teams.sort { |a, b| b.wins - b.losses <=> a.wins - a.losses }
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def initialize(season, start_time)
|
37
|
-
# The YAML file is included with the program,
|
38
|
-
# so this is as safe as anything else
|
39
|
-
@divisions = YAML.unsafe_load_file(File.expand_path('data/divisions.yaml',
|
40
|
-
File.dirname(__FILE__)))
|
41
|
-
|
42
|
-
@day = 0
|
43
|
-
@games_in_progress = []
|
44
|
-
@games = []
|
45
|
-
@champion_team = nil
|
46
|
-
@last_update_time = start_time
|
47
|
-
@passed_updates = 0
|
48
|
-
@prng = Random.new(69_420 * season)
|
49
|
-
@shuffled_teams = @divisions.values.reduce(:+).shuffle(random: @prng)
|
50
|
-
@playoff_teams = nil
|
51
|
-
@game_in_matchup = 3
|
52
|
-
end
|
53
|
-
|
54
|
-
def update_state
|
55
|
-
return if @champion_team
|
56
|
-
|
57
|
-
now = Time.at(Time.now.to_i)
|
58
|
-
five_sec_intervals = (now - @last_update_time).div(5)
|
59
|
-
|
60
|
-
return unless five_sec_intervals.positive?
|
61
|
-
|
62
|
-
five_sec_intervals.times do |i|
|
63
|
-
if ((i + @passed_updates) % 720).zero?
|
64
|
-
new_games
|
65
|
-
else
|
66
|
-
update_games
|
67
|
-
end
|
68
|
-
|
69
|
-
break if @champion_team
|
70
|
-
end
|
71
|
-
|
72
|
-
@last_update_time = now
|
73
|
-
@passed_updates += five_sec_intervals
|
74
|
-
end
|
75
|
-
|
76
|
-
private
|
77
|
-
|
78
|
-
def new_games
|
79
|
-
if @game_in_matchup != (@day > 38 ? 5 : 3)
|
80
|
-
# New game in matchups
|
81
|
-
@games.map! { |game| Game.new(game.away, game.home, @prng) }
|
82
|
-
@game_in_matchup += 1
|
83
|
-
return
|
84
|
-
end
|
85
|
-
|
86
|
-
# New matchups
|
87
|
-
@games.clear
|
88
|
-
@game_in_matchup = 1
|
89
|
-
@day += 1
|
90
|
-
|
91
|
-
case @day <=> 39
|
92
|
-
when -1
|
93
|
-
(@shuffled_teams.length / 2).times do |i|
|
94
|
-
pair = [@shuffled_teams[i], @shuffled_teams[-i - 1]]
|
95
|
-
@games << Game.new(*(@day > 19 ? pair : pair.reverse), @prng)
|
96
|
-
end
|
97
|
-
|
98
|
-
@shuffled_teams.insert 1, @shuffled_teams.pop
|
99
|
-
when 0
|
100
|
-
@playoff_teams = Team.sort(
|
101
|
-
@divisions.values.map do |teams|
|
102
|
-
Team.sort(teams).first(2)
|
103
|
-
end.reduce(:+)
|
104
|
-
).map(&:clone)
|
105
|
-
|
106
|
-
new_playoff_matchups
|
107
|
-
when 1
|
108
|
-
@playoff_teams = Team.sort(@playoff_teams).first(@playoff_teams.length / 2)
|
109
|
-
|
110
|
-
if @playoff_teams.length == 1
|
111
|
-
@champion_team = @playoff_teams[0]
|
112
|
-
return
|
113
|
-
end
|
114
|
-
|
115
|
-
new_playoff_matchups
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
def update_games
|
120
|
-
@games_in_progress.each(&:update)
|
121
|
-
@games_in_progress = @games.select(&:in_progress)
|
122
|
-
end
|
123
|
-
|
124
|
-
def new_playoff_matchups
|
125
|
-
@playoff_teams.each do |team|
|
126
|
-
team.wins = 0
|
127
|
-
team.losses = 0
|
128
|
-
end
|
129
|
-
|
130
|
-
(0...@playoff_teams.length).step(2) do |i|
|
131
|
-
@games << Game.new(*@playoff_teams[i, 2], @prng)
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
class Player
|
136
|
-
private_class_method(:new)
|
137
|
-
|
138
|
-
attr_reader(:stats, :to_s)
|
139
|
-
end
|
140
|
-
end
|
data/lib/messages.rb
DELETED
@@ -1,66 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class Messages
|
4
|
-
def initialize(event, fields, data)
|
5
|
-
@event = event
|
6
|
-
fields.zip(data) do |(f, d)|
|
7
|
-
instance_variable_set("@#{f}", d)
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
private_class_method(:new)
|
12
|
-
|
13
|
-
class << self
|
14
|
-
[
|
15
|
-
%i[StartOfGame title],
|
16
|
-
%i[EndOfGame winning_team],
|
17
|
-
%i[StartOfPeriod period],
|
18
|
-
%i[EndOfPeriod period home away home_score away_score],
|
19
|
-
%i[FaceOff winning_player],
|
20
|
-
%i[Hit puck_holder defender puck_taken],
|
21
|
-
%i[Pass sender receiver interceptor],
|
22
|
-
%i[Shoot shooter blocker puck_taken home away home_score away_score]
|
23
|
-
].each do |event, *fields|
|
24
|
-
define_method event do |*data|
|
25
|
-
new(event, fields, data)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def to_s
|
31
|
-
case @event
|
32
|
-
when :StartOfGame
|
33
|
-
"#{@title}\nHocky!"
|
34
|
-
when :EndOfGame
|
35
|
-
"Game over.\n#{@winning_team} wins!"
|
36
|
-
when :StartOfPeriod
|
37
|
-
"Start#{of_period}"
|
38
|
-
when :EndOfPeriod
|
39
|
-
"End#{of_period}#{score}"
|
40
|
-
when :FaceOff
|
41
|
-
"#{@winning_player} wins the faceoff!"
|
42
|
-
when :Hit
|
43
|
-
"#{@defender} hits #{@puck_holder.to_s + takes}!"
|
44
|
-
when :Pass
|
45
|
-
"#{@sender} passes to #{@receiver}" +
|
46
|
-
(@interceptor ? "... intercepted by #{@interceptor}!" : '.')
|
47
|
-
when :Shoot
|
48
|
-
"#{@shooter} takes a shot... " +
|
49
|
-
(@blocker ? "#{@blocker} blocks the shot#{takes}!" : "and scores!#{score}")
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
private
|
54
|
-
|
55
|
-
def of_period
|
56
|
-
" of period #{@period}."
|
57
|
-
end
|
58
|
-
|
59
|
-
def score
|
60
|
-
"\n#{@home} #{@home_score}, #{@away} #{@away_score}"
|
61
|
-
end
|
62
|
-
|
63
|
-
def takes
|
64
|
-
@puck_taken ? ' and takes the puck' : ''
|
65
|
-
end
|
66
|
-
end
|