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
@@ -0,0 +1,63 @@
|
|
1
|
+
require("hlockey/utils")
|
2
|
+
|
3
|
+
module Hlockey
|
4
|
+
class Player
|
5
|
+
attr_accessor(:stats)
|
6
|
+
attr_reader(:to_s)
|
7
|
+
|
8
|
+
@@cached_chain = nil
|
9
|
+
|
10
|
+
def initialize(prng = Random)
|
11
|
+
@to_s = make_name(prng)
|
12
|
+
# Generate stats
|
13
|
+
@stats = {}
|
14
|
+
%i[offense defense agility].each { |stat| @stats[stat] = prng.rand * 5 }
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def make_name(prng)
|
20
|
+
Array.new(2) do
|
21
|
+
combination = "__"
|
22
|
+
next_letter = ""
|
23
|
+
result = ""
|
24
|
+
|
25
|
+
10.times do
|
26
|
+
next_letters = name_chain[combination]
|
27
|
+
break if next_letters.nil?
|
28
|
+
|
29
|
+
cumulative_weights = []
|
30
|
+
next_letters.each_value do |v|
|
31
|
+
cumulative_weights << v + (cumulative_weights.last or 0)
|
32
|
+
end
|
33
|
+
|
34
|
+
next_letter = Utils.weighted_random(next_letters, prng)
|
35
|
+
break if next_letter == "_"
|
36
|
+
|
37
|
+
result += next_letter
|
38
|
+
combination = combination[1] + next_letter
|
39
|
+
end
|
40
|
+
|
41
|
+
result
|
42
|
+
end.join(" ")
|
43
|
+
end
|
44
|
+
|
45
|
+
def name_chain
|
46
|
+
if @@cached_chain.nil?
|
47
|
+
@@cached_chain = {}
|
48
|
+
File.open(File.expand_path("../../data/external/names.txt", __dir__)).each do |n|
|
49
|
+
name = "__#{n.chomp}__"
|
50
|
+
(name.length - 3).times do |i|
|
51
|
+
combination = name[i, 2]
|
52
|
+
next_letter = name[i + 2]
|
53
|
+
@@cached_chain[combination] ||= {}
|
54
|
+
@@cached_chain[combination][next_letter] ||= 0
|
55
|
+
@@cached_chain[combination][next_letter] += 1
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
@@cached_chain
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/hlockey/team.rb
ADDED
@@ -0,0 +1,59 @@
|
|
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)
|
7
|
+
attr_reader(:to_s, :emoji)
|
8
|
+
|
9
|
+
# rand is only used if roster is nil
|
10
|
+
def initialize(name, emoji, roster: nil, random: Random)
|
11
|
+
@to_s = name
|
12
|
+
@emoji = emoji
|
13
|
+
|
14
|
+
if roster.nil?
|
15
|
+
@roster = {}
|
16
|
+
%i[lwing center rwing ldef goalie rdef].each do |pos|
|
17
|
+
@roster[pos] = Player.new(random)
|
18
|
+
end
|
19
|
+
else
|
20
|
+
@roster = roster
|
21
|
+
end
|
22
|
+
|
23
|
+
@wins = 0
|
24
|
+
@losses = 0
|
25
|
+
end
|
26
|
+
|
27
|
+
def random_player(random, filter_predicate = nil)
|
28
|
+
players = @roster.values
|
29
|
+
players.select!(&filter_predicate) unless filter_predicate.nil?
|
30
|
+
players.sample(random: random)
|
31
|
+
end
|
32
|
+
|
33
|
+
def w_l
|
34
|
+
"#{@wins}-#{@losses}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def worst_player_pos
|
38
|
+
@roster.transform_values do |player|
|
39
|
+
player.stats.values.reduce(:+)
|
40
|
+
end.min_by { |k, v| v }.first
|
41
|
+
end
|
42
|
+
|
43
|
+
# Takes symbol from Team roster & converts it to full position name
|
44
|
+
def self.pos_name(pos)
|
45
|
+
case pos
|
46
|
+
when :lwing
|
47
|
+
"Left wing"
|
48
|
+
when :rwing
|
49
|
+
"Right wing"
|
50
|
+
when :ldef
|
51
|
+
"Left defender"
|
52
|
+
when :rdef
|
53
|
+
"Right defender"
|
54
|
+
else
|
55
|
+
pos.capitalize.to_s
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,12 @@
|
|
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
|
+
def self.weighted_random(hash, prng = Random)
|
7
|
+
cumulative_weights = []
|
8
|
+
hash.each_value { |v| cumulative_weights << v + (cumulative_weights.last or 0) }
|
9
|
+
hash.keys[cumulative_weights.index { |n| prng.rand(cumulative_weights.last) < n }]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,114 @@
|
|
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
|
+
end
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
self.class.to_s.split("::").last
|
13
|
+
end
|
14
|
+
|
15
|
+
def on_game_start() end
|
16
|
+
|
17
|
+
def on_game_end() end
|
18
|
+
|
19
|
+
def on_period_end() end
|
20
|
+
|
21
|
+
def on_action() end
|
22
|
+
|
23
|
+
def on_goal(scoring_team) end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def worst_player_rand_replace(message, apprx_times_per_game = 1, store_old_in = nil)
|
28
|
+
return unless @game.prng.rand(Game::NON_OT_ACTIONS / apprx_times_per_game).zero?
|
29
|
+
|
30
|
+
team = @game.prng.rand(2).zero? ? @game.home : @game.away
|
31
|
+
pos = team.worst_player_pos
|
32
|
+
|
33
|
+
prev_player = team.roster[pos]
|
34
|
+
next_player = Player.new(@game.prng)
|
35
|
+
|
36
|
+
team.roster[pos] = next_player
|
37
|
+
store_old_in[team][pos] ||= prev_player unless store_old_in.nil?
|
38
|
+
|
39
|
+
@game.stream << Message.send(message, prev_player, next_player)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class Chicken < Weather
|
44
|
+
def on_game_start
|
45
|
+
@chickened_out = { @game.home => {}, @game.away => {} }
|
46
|
+
end
|
47
|
+
|
48
|
+
def on_action
|
49
|
+
worst_player_rand_replace(:ChickenedOut, 5, @chickened_out)
|
50
|
+
end
|
51
|
+
|
52
|
+
def on_game_end
|
53
|
+
@chickened_out.each { |team, players| team.roster.merge!(players) }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class Inclines < Weather
|
58
|
+
def on_game_start
|
59
|
+
make_incline
|
60
|
+
end
|
61
|
+
|
62
|
+
def on_goal(scoring_team)
|
63
|
+
return unless scoring_team == @favored_team
|
64
|
+
|
65
|
+
@game.score[scoring_team] += @game.prng.rand(0.1..0.5)
|
66
|
+
make_incline
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def make_incline
|
72
|
+
new_favored_team = @game.prng.rand(2).zero? ? :home : :away
|
73
|
+
return unless new_favored_team != @favored_team
|
74
|
+
|
75
|
+
@favored_team = new_favored_team
|
76
|
+
@game.stream << Message.InclineFavors(@game.send(@favored_team))
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
class Stars < Weather
|
81
|
+
def on_period_end
|
82
|
+
return unless @game.score[:home] != @game.score[:away]
|
83
|
+
|
84
|
+
losing_team = @game.score.min_by { |_k, v| v }.first
|
85
|
+
@game.score[losing_team] += 0.5
|
86
|
+
@game.stream << Message.StarsPity(@game.send(losing_team))
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
class Sunset < Weather
|
91
|
+
def on_game_start
|
92
|
+
@score_amt = { home: 0, away: 0 }
|
93
|
+
end
|
94
|
+
|
95
|
+
def on_goal(scoring_team)
|
96
|
+
@score_amt[scoring_team] += 1
|
97
|
+
@game.score[scoring_team] -= @score_amt[scoring_team] * 0.2
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
class Waves < Weather
|
102
|
+
def on_action
|
103
|
+
worst_player_rand_replace(:WavesWashedAway)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
Weather::WEIGHTS = {
|
108
|
+
Chicken => 4,
|
109
|
+
Inclines => 4,
|
110
|
+
Stars => 4,
|
111
|
+
Sunset => 4,
|
112
|
+
Waves => 3
|
113
|
+
}.freeze
|
114
|
+
end
|
data/lib/hlockey.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require("hlockey/league")
|
metadata
CHANGED
@@ -1,33 +1,44 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hlockey
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '
|
4
|
+
version: '3'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lavender Perry
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-06-02 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
|
-
description:
|
13
|
+
description: Hlockey library.
|
14
14
|
email: endie2@protonmail.com
|
15
|
-
executables:
|
16
|
-
- hlockey
|
15
|
+
executables: []
|
17
16
|
extensions: []
|
18
17
|
extra_rdoc_files: []
|
19
18
|
files:
|
20
|
-
-
|
21
|
-
-
|
22
|
-
-
|
23
|
-
-
|
24
|
-
-
|
25
|
-
- lib/
|
26
|
-
|
19
|
+
- data/election.yaml
|
20
|
+
- data/external/names.txt
|
21
|
+
- data/information.yaml
|
22
|
+
- data/league.yaml
|
23
|
+
- data/links.yaml
|
24
|
+
- lib/hlockey.rb
|
25
|
+
- lib/hlockey/constants.rb
|
26
|
+
- lib/hlockey/data.rb
|
27
|
+
- lib/hlockey/game.rb
|
28
|
+
- lib/hlockey/game/actions.rb
|
29
|
+
- lib/hlockey/game/fight.rb
|
30
|
+
- lib/hlockey/league.rb
|
31
|
+
- lib/hlockey/message.rb
|
32
|
+
- lib/hlockey/player.rb
|
33
|
+
- lib/hlockey/team.rb
|
34
|
+
- lib/hlockey/utils.rb
|
35
|
+
- lib/hlockey/version.rb
|
36
|
+
- lib/hlockey/weather.rb
|
37
|
+
homepage: https://github.com/Hlockey/lib
|
27
38
|
licenses:
|
28
39
|
- LicenseRef-LICENSE.md
|
29
40
|
metadata:
|
30
|
-
source_code_uri: https://github.com/
|
41
|
+
source_code_uri: https://github.com/Hlockey/lib
|
31
42
|
post_install_message:
|
32
43
|
rdoc_options: []
|
33
44
|
require_paths:
|
@@ -43,8 +54,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
43
54
|
- !ruby/object:Gem::Version
|
44
55
|
version: '0'
|
45
56
|
requirements: []
|
46
|
-
rubygems_version: 3.
|
57
|
+
rubygems_version: 3.4.10
|
47
58
|
signing_key:
|
48
59
|
specification_version: 4
|
49
|
-
summary: Hlockey season
|
60
|
+
summary: Hlockey season 3.
|
50
61
|
test_files: []
|
data/bin/hlockey
DELETED
@@ -1,90 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
# frozen_string_literal: true
|
4
|
-
|
5
|
-
require_relative('../lib/league')
|
6
|
-
require_relative('../lib/data/season')
|
7
|
-
|
8
|
-
def menu_input(default, *choices)
|
9
|
-
puts('Please enter a number...')
|
10
|
-
# Adding 1 to i when printing choices
|
11
|
-
# to avoid confusing a non-numerical input with the first choice
|
12
|
-
choices.each_with_index { |choice, i| puts("#{(i + 1).to_s.rjust(2)} - #{choice}") }
|
13
|
-
puts("anything else - #{default}")
|
14
|
-
|
15
|
-
# Subtract 1 here to undo adding 1 earlier
|
16
|
-
gets.to_i - 1
|
17
|
-
end
|
18
|
-
|
19
|
-
# Each element from choices should implement to_s
|
20
|
-
def menu_input_elem(default, choices)
|
21
|
-
i = menu_input(default, *choices)
|
22
|
-
choices[i] unless i.negative?
|
23
|
-
end
|
24
|
-
|
25
|
-
PUTS = method(:puts)
|
26
|
-
league = League.new(Season::NUMBER, Season::START_TIME)
|
27
|
-
|
28
|
-
if Time.now < Season::START_TIME
|
29
|
-
puts Season::START_TIME
|
30
|
-
.strftime("Season #{Season::NUMBER} starts at %H:%M on %A, %B %d.")
|
31
|
-
end
|
32
|
-
|
33
|
-
loop do
|
34
|
-
league.update_state
|
35
|
-
|
36
|
-
puts("Season #{Season::NUMBER} day #{league.day}")
|
37
|
-
|
38
|
-
case menu_input('Exit', 'Games', 'Standings', 'Rosters', 'Election')
|
39
|
-
when 0 # Games
|
40
|
-
if league.games_in_progress.empty?
|
41
|
-
puts('There are currently no games being played.')
|
42
|
-
next
|
43
|
-
end
|
44
|
-
|
45
|
-
game = menu_input_elem('Back', league.games_in_progress)
|
46
|
-
next if game.nil?
|
47
|
-
|
48
|
-
loop do
|
49
|
-
league.update_state
|
50
|
-
|
51
|
-
game.stream.each(&PUTS)
|
52
|
-
|
53
|
-
break unless game.in_progress
|
54
|
-
|
55
|
-
game.stream.clear
|
56
|
-
sleep(5)
|
57
|
-
end
|
58
|
-
when 1 # Standings
|
59
|
-
if league.champion_team
|
60
|
-
puts("Your season #{Season::NUMBER} champions are the "\
|
61
|
-
"#{league.champion_team.name}!")
|
62
|
-
elsif league.playoff_teams
|
63
|
-
puts('Playoffs')
|
64
|
-
league.playoff_teams.each(&:print_win_loss)
|
65
|
-
end
|
66
|
-
|
67
|
-
league.divisions.each do |name, teams|
|
68
|
-
puts(name)
|
69
|
-
League::Team.sort(teams).each(&:print_win_loss)
|
70
|
-
end
|
71
|
-
when 2 # Rosters
|
72
|
-
teams = league.divisions.values.reduce(:+)
|
73
|
-
team = menu_input_elem('All teams', teams)
|
74
|
-
|
75
|
-
if team.nil?
|
76
|
-
teams.each(&:print_roster)
|
77
|
-
next
|
78
|
-
end
|
79
|
-
team.print_roster
|
80
|
-
when 3 # Election
|
81
|
-
Season::ELECTION.each do |category, options|
|
82
|
-
puts(category)
|
83
|
-
options.each { |name, description| puts(" #{name}: #{description}") }
|
84
|
-
end
|
85
|
-
puts("Go to #{Season::ELECTION_FORM} to vote.\n"\
|
86
|
-
"If you don't want to use Google Forms, DM me on Discord (Lavender#9223).")
|
87
|
-
else
|
88
|
-
exit
|
89
|
-
end
|
90
|
-
end
|