hlockey 2 → 4

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