hlockey 1 → 2

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