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