hlockey 2 → 4

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.
data/lib/hlockey/team.rb CHANGED
@@ -1,35 +1,52 @@
1
- # frozen_string_literal: true
2
-
3
- module Hlockey
4
- # Created by League when data is loaded
5
- class Team
6
- private_class_method(:new)
7
- attr_accessor(:wins, :losses, :roster)
8
- attr_reader(:to_s, :emoji)
9
-
10
- def w_l
11
- "#{@wins}-#{@losses}"
12
- end
13
-
14
- # Created by League when data is loaded
15
- class Player
16
- attr_accessor(:stats, :to_s)
17
- end
18
- end
19
-
20
- # Takes symbol from Team roster & converts it to full position name
21
- def Hlockey.pos_name(pos)
22
- case pos
23
- when :lwing
24
- 'Left wing'
25
- when :rwing
26
- 'Right wing'
27
- when :ldef
28
- 'Left defender'
29
- when :rdef
30
- 'Right defender'
31
- else
32
- pos.capitalize.to_s
33
- end
34
- end
35
- end
1
+ require("hlockey/player")
2
+
3
+ module Hlockey
4
+ # Created by League when data is loaded
5
+ class Team
6
+ attr_accessor(:wins, :losses, :roster, :shadows, :status, :to_s)
7
+ attr_reader(:emoji, :color, :motto)
8
+
9
+ # random is only used if roster is nil
10
+ def initialize(name, color, emoji, roster: nil, shadows: nil, random: Random)
11
+ @to_s = name
12
+ @color = color
13
+ @emoji = emoji
14
+ @roster = if roster.nil?
15
+ %i[lwing center rwing ldef goalie rdef].each_with_object({}) do |pos, h|
16
+ h[pos] = Player.new(self, random)
17
+ end
18
+ else
19
+ roster
20
+ end
21
+ @shadows = shadows.nil? ? Array.new(6) { Player.new(random) } : shadows
22
+ @wins = 0
23
+ @losses = 0
24
+ end
25
+
26
+ def w_l
27
+ "#{@wins}-#{@losses}"
28
+ end
29
+
30
+ def worst_player_pos
31
+ @roster.transform_values { |p| p.stats.values.reduce(:+) }
32
+ .min_by { |_, v| v }
33
+ .first
34
+ end
35
+
36
+ # Takes symbol from Team roster & converts it to full position name
37
+ def self.pos_name(pos)
38
+ case pos
39
+ when :lwing
40
+ "Left wing"
41
+ when :rwing
42
+ "Right wing"
43
+ when :ldef
44
+ "Left defender"
45
+ when :rdef
46
+ "Right defender"
47
+ else
48
+ pos.capitalize.to_s
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,15 @@
1
+ module Hlockey
2
+ module Utils
3
+ # Does a weighted random with a hash,
4
+ # where the keys are elements being randomly selected,
5
+ # and the values are the weights
6
+ # @param hash [Hash<Object => Integer>]
7
+ # @param prng [#rand]
8
+ # @return [Object]
9
+ def self.weighted_random(hash, prng)
10
+ cumulative_weights = []
11
+ hash.each_value { |v| cumulative_weights << v + (cumulative_weights.last or 0) }
12
+ hash.keys[cumulative_weights.index { |n| prng.rand(cumulative_weights.last) < n }]
13
+ end
14
+ end
15
+ end
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
- module Hlockey
4
- VERSION = '2'
5
- end
1
+ module Hlockey
2
+ VERSION = "4".freeze
3
+ end
@@ -0,0 +1,168 @@
1
+ require("hlockey/game")
2
+ require("hlockey/message")
3
+ require("hlockey/player")
4
+
5
+ module Hlockey
6
+ class Weather
7
+ def initialize(game)
8
+ @game = game
9
+ @home = @game.home
10
+ @away = @game.away
11
+ @prng = @game.prng
12
+ end
13
+
14
+ def to_s
15
+ self.class.to_s.split("::").last
16
+ end
17
+
18
+ def on_game_start() end
19
+
20
+ def on_game_end() end
21
+
22
+ def on_period_end() end
23
+
24
+ def on_action() end
25
+
26
+ def on_goal(scoring_team) end
27
+
28
+ private
29
+
30
+ # Swaps roster & shadows player, used for Chicken & Waves weather
31
+ # @param team_sym [:home, :away]
32
+ # @param pos [Symbol] position of player swapped out
33
+ # @param shadows_idx [Integer] index of shadows player swapped in
34
+ # @return [Array<Player>] the players that were swapped
35
+ def swap(team_sym, pos, shadows_idx)
36
+ team = @game.send(team_sym)
37
+
38
+ prev_player = team.roster[pos]
39
+ next_player = team.shadows[shadows_idx]
40
+ team.roster[pos] = next_player
41
+ team.shadows[shadows_idx] = prev_player
42
+
43
+ unless @game.fight.nil?
44
+ player_array = @game.fight.players[team_sym]
45
+ player_idx = player_array.index(prev_player)
46
+
47
+ player_array[player_idx] = next_player unless player_idx.nil?
48
+ end
49
+
50
+ [prev_player, next_player]
51
+ end
52
+
53
+ # @return [:home, :away]
54
+ def rand_team
55
+ @prng.rand(2).zero? ? :home : :away
56
+ end
57
+ end
58
+
59
+ class Audacity < Weather
60
+ def on_action
61
+ @game.shoot(audacity: true) if @prng.rand(Game::NON_OT_ACTIONS / 10).zero?
62
+ end
63
+ end
64
+
65
+ class Chicken < Weather
66
+ def on_game_start
67
+ @chickened_out = { home: {}, away: {} }
68
+ end
69
+
70
+ def on_action
71
+ return unless @prng.rand(Game::NON_OT_ACTIONS / 5).zero?
72
+
73
+ team = rand_team
74
+ pos, shadows_idx = get_swap(team)
75
+ if pos.nil? || shadows_idx.nil?
76
+ team = team == :home ? :away : :home
77
+ pos, shadows_idx = get_swap(team)
78
+ return if pos.nil? || shadows_idx.nil?
79
+ end
80
+
81
+ @chickened_out[team][pos] = shadows_idx
82
+
83
+ @game.stream << Message.ChickenedOut(*swap(team, pos, shadows_idx))
84
+ end
85
+
86
+ def on_game_end
87
+ @chickened_out.each do |team, swaps|
88
+ swaps.each { |pos, shadows_idx| swap(team, pos, shadows_idx) }
89
+ end
90
+ end
91
+
92
+ private
93
+
94
+ # @param team_sym [:home, :away]
95
+ # @return [Array<Symbol, Integer>]
96
+ def get_swap(team_sym)
97
+ team = @game.send(team_sym)
98
+
99
+ [
100
+ (team.roster.keys - @chickened_out[team_sym].keys).sample(random: @prng),
101
+ (team.shadows.each_index.to_a - @chickened_out[team_sym].values)
102
+ .sample(random: @prng)
103
+ ]
104
+ end
105
+ end
106
+
107
+ class Inclines < Weather
108
+ def on_game_start
109
+ make_incline
110
+ end
111
+
112
+ def on_goal(scoring_team)
113
+ return unless scoring_team == @favored_team
114
+
115
+ @game.score[scoring_team] += @prng.rand(0.1..0.5)
116
+ make_incline
117
+ end
118
+
119
+ private
120
+
121
+ def make_incline
122
+ new_favored_team = rand_team
123
+ return unless new_favored_team != @favored_team
124
+
125
+ @favored_team = new_favored_team
126
+ @game.stream << Message.InclineFavors(@game.send(@favored_team))
127
+ end
128
+ end
129
+
130
+ class Stars < Weather
131
+ def on_period_end
132
+ team = %i[home away].find { |sym| @game.send(sym).to_s == "Sleepers" }
133
+ if team.nil?
134
+ return if @game.score[:home] == @game.score[:away]
135
+
136
+ team = @game.score.min_by { |_, v| v }.first
137
+ end
138
+
139
+ @game.score[team] += 0.5
140
+ @game.stream << Message.StarsAlign(@game.send(team))
141
+ end
142
+ end
143
+
144
+ class Sunset < Weather
145
+ def on_game_start
146
+ @score_amt = { home: 0, away: 0 }
147
+ end
148
+
149
+ def on_goal(scoring_team)
150
+ @score_amt[scoring_team] += 1
151
+ @game.score[scoring_team] -= @score_amt[scoring_team] * 0.2
152
+ end
153
+ end
154
+
155
+ class Waves < Weather
156
+ def on_action
157
+ return unless @prng.rand(Game::NON_OT_ACTIONS).zero?
158
+
159
+ team_sym = rand_team
160
+ team = @game.send(team_sym)
161
+ @game.stream << Message.WavesWashedAway(*swap(team_sym,
162
+ team.worst_player_pos,
163
+ @prng.rand(team.shadows.length)))
164
+ end
165
+ end
166
+
167
+ Weather::WEATHERS = [Audacity, Chicken, Inclines, Stars, Sunset, Waves].freeze
168
+ end
data/lib/hlockey.rb CHANGED
@@ -1,3 +1 @@
1
- # frozen_string_literal: true
2
-
3
- require('hlockey/league')
1
+ require("hlockey/league")
metadata CHANGED
@@ -1,39 +1,59 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hlockey
3
3
  version: !ruby/object:Gem::Version
4
- version: '2'
4
+ version: '4'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lavender Perry
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-08-08 00:00:00.000000000 Z
12
- dependencies: []
13
- description: Hlockey library and console application.
11
+ date: 2023-07-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: psych
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '5'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '5'
27
+ description: Hlockey library.
14
28
  email: endie2@protonmail.com
15
- executables:
16
- - hlockey
29
+ executables: []
17
30
  extensions: []
18
31
  extra_rdoc_files: []
19
32
  files:
20
- - bin/hlockey
33
+ - data/election.yaml
34
+ - data/external/names.txt
35
+ - data/information.yaml
36
+ - data/league.yaml
37
+ - data/links.yaml
38
+ - data/previous_election_results.yaml
21
39
  - lib/hlockey.rb
40
+ - lib/hlockey/constants.rb
22
41
  - lib/hlockey/data.rb
23
- - lib/hlockey/data/election.yaml
24
- - lib/hlockey/data/information.yaml
25
- - lib/hlockey/data/league.yaml
26
- - lib/hlockey/data/links.yaml
27
42
  - lib/hlockey/game.rb
43
+ - lib/hlockey/game/actions.rb
44
+ - lib/hlockey/game/fight.rb
28
45
  - lib/hlockey/league.rb
29
- - lib/hlockey/messages.rb
46
+ - lib/hlockey/message.rb
47
+ - lib/hlockey/player.rb
30
48
  - lib/hlockey/team.rb
49
+ - lib/hlockey/utils.rb
31
50
  - lib/hlockey/version.rb
32
- homepage: https://hlockey.herokuapp.com
51
+ - lib/hlockey/weather.rb
52
+ homepage: https://github.com/Hlockey/lib
33
53
  licenses:
34
54
  - LicenseRef-LICENSE.md
35
55
  metadata:
36
- source_code_uri: https://github.com/Hlockey/hlockey
56
+ source_code_uri: https://github.com/Hlockey/lib
37
57
  post_install_message:
38
58
  rdoc_options: []
39
59
  require_paths:
@@ -49,8 +69,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
49
69
  - !ruby/object:Gem::Version
50
70
  version: '0'
51
71
  requirements: []
52
- rubygems_version: 3.3.7
72
+ rubygems_version: 3.4.10
53
73
  signing_key:
54
74
  specification_version: 4
55
- summary: Hlockey season 2.
75
+ summary: Hlockey season 4.
56
76
  test_files: []
data/bin/hlockey DELETED
@@ -1,104 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- # frozen_string_literal: true
4
-
5
- require('hlockey')
6
-
7
- def menu_input(default, *choices)
8
- puts('Please enter a number...')
9
- # Adding 1 to i when printing choices
10
- # to avoid confusing a non-numerical input with the first choice
11
- choices.each_with_index { |choice, i| puts("#{(i + 1).to_s.rjust(2)} - #{choice}") }
12
- puts("anything else - #{default}")
13
-
14
- # Subtract 1 here to undo adding 1 earlier
15
- STDIN.gets.to_i - 1
16
- end
17
-
18
- # Each element from choices should implement to_s
19
- def menu_input_elem(choices)
20
- i = menu_input('Back', *choices)
21
- choices[i] unless i.negative?
22
- end
23
-
24
- def emoji_team(team)
25
- "#{team.emoji} #{team}"
26
- end
27
-
28
- puts('Please wait...')
29
-
30
- PRINT_TEAM_W_L = Proc.new do |team|
31
- puts(" #{emoji_team(team).ljust(26)} #{team.w_l}")
32
- end
33
-
34
- league = Hlockey::League.new
35
-
36
- puts(Hlockey::Messages.SeasonStarts(league.start_time)) if Time.now < league.start_time
37
-
38
- loop do
39
- league.update_state
40
-
41
- puts(Hlockey::Messages.SeasonDay(league.day))
42
-
43
- case menu_input('Exit', 'Games', 'Standings', 'Team info', 'Election', 'Information')
44
- when 0 # Games
45
- if league.games.empty?
46
- puts(Hlockey::Messages.NoGames)
47
- next
48
- end
49
-
50
- game = menu_input_elem(league.games)
51
- next if game.nil?
52
-
53
- puts("#{game.home.emoji} #{game} #{game.away.emoji}")
54
- loop do
55
- league.update_state
56
-
57
- game.stream.each(&method(:puts))
58
-
59
- break unless game.in_progress
60
-
61
- game.stream.clear
62
- sleep(5)
63
- end
64
- when 1 # Standings
65
- if league.champion_team
66
- puts(Hlockey::Messages.SeasonChampion(league.champion_team))
67
- elsif league.playoff_teams
68
- puts('Playoffs')
69
- league.playoff_teams.each(&PRINT_TEAM_W_L)
70
- end
71
-
72
- league.divisions.each do |name, teams|
73
- puts(name)
74
- teams.each(&PRINT_TEAM_W_L)
75
- end
76
- when 2 # Team info
77
- team = menu_input_elem(league.teams)
78
- next if team.nil?
79
-
80
- puts(emoji_team(team))
81
- team.roster.each do |pos, player|
82
- puts(" #{Hlockey.pos_name(pos)}:\n #{player}")
83
- player.stats.each { |stat, value| puts(" #{stat}: #{value.round(1)}") }
84
- end
85
- puts(" wins: #{team.wins}")
86
- puts(" losses: #{team.losses}")
87
- when 3 # Election
88
- election = Hlockey.load_data('election')
89
- election.each do |category, options|
90
- puts(category)
91
- options.each { |name, description| puts(" #{name}: #{description}") }
92
- end
93
- puts('Go to the website (https://hlockey.herokuapp.com) to vote.')
94
- when 4 # Information
95
- Hlockey.load_data('information').each do |title, info|
96
- puts(title)
97
- info.each_line { |l| puts(" #{l}") }
98
- end
99
- puts('Links')
100
- Hlockey.load_data('links').each { |site, link| puts(" #{site}: #{link}") }
101
- else
102
- exit
103
- end
104
- end
@@ -1,16 +0,0 @@
1
- ---
2
- :Bribery:
3
- :Flavor Text: Give each team a motto.
4
- :Weather: Move all the stadiums outdoors.
5
- :We Do A Little Losing: Recognize the losingest teams with an Underbracket.
6
- :Treasure:
7
- :Vengabus: All your team's stats are boosted by your team's losses * 0.005.
8
- :Dave: The worst stat on your team is set to 4.5.
9
- :Nice: Boost your worst player by 0.69 in every stat.
10
- :Convenient Math Error: Your team starts the next season with 5 wins.
11
- :Coaching:
12
- :Please Block Shots: Move your team's best defensive player to goalie.
13
- :Please Win Faceoffs: Move your team's player best offensive player to center.
14
- :Draft: Replace your team's worst player with a random new player.
15
- :Small Gamble: All your team's stats go up or down by 0.5 at random.
16
- :Big Gamble: All your team's stats go up or down by 1.0 at random.
@@ -1,12 +0,0 @@
1
- ---
2
- :Format: |-
3
- Hlockey has 3 game long regular season matchups, & 5 game long postseason matchups.
4
- The regular season is round robin format.
5
- Teams play 2 regular season matchups against each other.
6
- Playoffs are single-elimination tournaments.
7
- The top 2 teams of each division qualify for playoffs.
8
- Playoffs are perfectly seeded (best team plays worst team, etc).
9
- :Relation to Blaseball: |-
10
- Both Hlockey and Blaseball are splorts, therefore they have some similarities.
11
- Breaks will usually be scheduled when Blaseball is more active.
12
- Hlockey is inspired by Blaseball, & would most likely not exist without it.