hlockey 0 → 2
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/bin/hlockey +62 -40
- data/lib/hlockey/data/election.yaml +16 -0
- data/lib/hlockey/data/information.yaml +12 -0
- data/lib/hlockey/data/league.yaml +846 -0
- data/lib/hlockey/data/links.yaml +2 -0
- data/lib/hlockey/data.rb +13 -0
- data/lib/hlockey/game.rb +159 -0
- data/lib/hlockey/league.rb +115 -0
- data/lib/hlockey/messages.rb +100 -0
- data/lib/hlockey/team.rb +35 -0
- data/lib/hlockey/version.rb +5 -0
- data/lib/hlockey.rb +3 -0
- metadata +17 -12
- data/lib/data/names_markov_chain.rb +0 -634
- data/lib/game.rb +0 -153
- data/lib/league.rb +0 -130
- data/lib/messages.rb +0 -64
- data/lib/player.rb +0 -41
- data/lib/team.rb +0 -46
data/lib/hlockey/data.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require('yaml')
|
4
|
+
|
5
|
+
module Hlockey
|
6
|
+
# Loads data for the current Hlockey season.
|
7
|
+
# +category+ can be 'league', 'election', 'information', or 'links'.
|
8
|
+
# If it is anything else, there will be an error.
|
9
|
+
def Hlockey.load_data(category)
|
10
|
+
# If this is only used on data included with the gem, it should be safe
|
11
|
+
YAML.unsafe_load_file(File.expand_path("data/#{category}.yaml", __dir__))
|
12
|
+
end
|
13
|
+
end
|
data/lib/hlockey/game.rb
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require('hlockey/messages')
|
4
|
+
|
5
|
+
module Hlockey
|
6
|
+
class Game
|
7
|
+
attr_reader(:home, :away, :stream, :score, :in_progress)
|
8
|
+
|
9
|
+
def initialize(home, away, prng)
|
10
|
+
@home = home
|
11
|
+
@away = away
|
12
|
+
@prng = prng
|
13
|
+
@stream = [Messages.StartOfGame]
|
14
|
+
@score = { home: 0, away: 0 }
|
15
|
+
@in_progress = true
|
16
|
+
@actions = 0
|
17
|
+
@period = 1
|
18
|
+
@face_off = true
|
19
|
+
@team_with_puck = nil
|
20
|
+
@puckless_team = nil
|
21
|
+
@puck_holder = nil
|
22
|
+
@shooting_chance = 0
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_s
|
26
|
+
"#{@home} vs #{@away}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def update
|
30
|
+
return unless @in_progress
|
31
|
+
|
32
|
+
@stream << Messages.StartOfPeriod(@period) if @actions.zero?
|
33
|
+
@actions += 1
|
34
|
+
|
35
|
+
if @actions == 60
|
36
|
+
@stream << Messages.EndOfPeriod(@period, @home, @away, *@score.values)
|
37
|
+
@actions = 0
|
38
|
+
@period += 1
|
39
|
+
start_faceoff
|
40
|
+
|
41
|
+
if @period > 3 && !(@score[:home] == @score[:away] && @period < 12)
|
42
|
+
# Game is over
|
43
|
+
@in_progress = false
|
44
|
+
|
45
|
+
winner, loser = @score[:away] > @score[:home] ?
|
46
|
+
[@away, @home] : [@home, @away]
|
47
|
+
|
48
|
+
@stream << Messages.EndOfGame(winner)
|
49
|
+
winner.wins += 1
|
50
|
+
loser.losses += 1
|
51
|
+
end
|
52
|
+
return
|
53
|
+
end
|
54
|
+
|
55
|
+
if @face_off
|
56
|
+
if @puck_holder.nil?
|
57
|
+
# Pass opposite team to who wins the puck to switch_team_with_puck,
|
58
|
+
# so @team_with_puck & @puckless_team are set to the correct values.
|
59
|
+
switch_team_with_puck(
|
60
|
+
action_succeeds?(@home.roster[:center].stats[:offense],
|
61
|
+
@away.roster[:center].stats[:offense]) ? @away : @home
|
62
|
+
)
|
63
|
+
|
64
|
+
@puck_holder = @team_with_puck.roster[:center]
|
65
|
+
@stream << Messages.FaceOff(@puck_holder, @team_with_puck)
|
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
|
+
check
|
78
|
+
else # Shoot
|
79
|
+
unless @shooting_chance < 5 &&
|
80
|
+
try_block_shot(@puckless_team.roster[@prng.rand(2).zero? ?
|
81
|
+
:ldef : :rdef]) ||
|
82
|
+
try_block_shot(@puckless_team.roster[:goalie])
|
83
|
+
@score[@team_with_puck == @home ? :home : :away] += 1
|
84
|
+
|
85
|
+
@stream << Messages.ShootScore(@puck_holder, @home, @away, *@score.values)
|
86
|
+
|
87
|
+
start_faceoff
|
88
|
+
@actions = 59 if @period > 3 # Sudden death overtime
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def action_succeeds?(helping_stat, hindering_stat)
|
96
|
+
@prng.rand(10) + helping_stat - hindering_stat > 4
|
97
|
+
end
|
98
|
+
|
99
|
+
def start_faceoff
|
100
|
+
@shooting_chance = 0
|
101
|
+
@face_off = true
|
102
|
+
@puck_holder = nil
|
103
|
+
end
|
104
|
+
|
105
|
+
def switch_team_with_puck(team_with_puck = @team_with_puck)
|
106
|
+
@team_with_puck, @puckless_team = team_with_puck == @home ?
|
107
|
+
[@away, @home] : [@home, @away]
|
108
|
+
@shooting_chance = 0
|
109
|
+
end
|
110
|
+
|
111
|
+
def pass
|
112
|
+
sender = @puck_holder
|
113
|
+
receiver = puckless_non_goalie(@team_with_puck)
|
114
|
+
interceptor = puckless_non_goalie
|
115
|
+
|
116
|
+
if !@face_off && try_take_puck(interceptor, 4) # Pass intercepted
|
117
|
+
@stream << Messages.Pass(sender, receiver, interceptor, @team_with_puck)
|
118
|
+
return
|
119
|
+
end
|
120
|
+
|
121
|
+
@stream << Messages.Pass(sender, receiver)
|
122
|
+
@puck_holder = receiver
|
123
|
+
@shooting_chance += 1
|
124
|
+
end
|
125
|
+
|
126
|
+
def check
|
127
|
+
defender = puckless_non_goalie
|
128
|
+
@stream << Messages.Hit(@puck_holder, defender,
|
129
|
+
try_take_puck(defender, 0, :defense), @team_with_puck)
|
130
|
+
end
|
131
|
+
|
132
|
+
def try_take_puck(player, dis = 0, stat = :agility)
|
133
|
+
return false unless action_succeeds?(player.stats[stat] - dis,
|
134
|
+
@puck_holder.stats[stat])
|
135
|
+
|
136
|
+
switch_team_with_puck
|
137
|
+
@puck_holder = player
|
138
|
+
|
139
|
+
true
|
140
|
+
end
|
141
|
+
|
142
|
+
def try_block_shot(blocker)
|
143
|
+
return false unless action_succeeds?(blocker.stats[:defense],
|
144
|
+
@puck_holder.stats[:offense])
|
145
|
+
|
146
|
+
@shooting_chance += 1
|
147
|
+
@stream << Messages.ShootBlock(@puck_holder, blocker,
|
148
|
+
try_take_puck(blocker), @team_with_puck)
|
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
|
159
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require('hlockey/data')
|
4
|
+
require('hlockey/game')
|
5
|
+
require('hlockey/team')
|
6
|
+
require('hlockey/version')
|
7
|
+
|
8
|
+
module Hlockey
|
9
|
+
class League
|
10
|
+
attr_reader(:start_time, :divisions, :teams, :day,
|
11
|
+
:games, :games_in_progress, :playoff_teams, :champion_team)
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@start_time, @divisions = Hlockey.load_data('league')
|
15
|
+
@start_time.localtime
|
16
|
+
@day = 0
|
17
|
+
@games_in_progress = []
|
18
|
+
@games = []
|
19
|
+
@champion_team = nil
|
20
|
+
@last_update_time = @start_time
|
21
|
+
@passed_updates = 0
|
22
|
+
@prng = Random.new(69_420 * VERSION.to_i)
|
23
|
+
@teams = @divisions.values.reduce(:+)
|
24
|
+
@shuffled_teams = teams.shuffle(random: @prng)
|
25
|
+
@playoff_teams = nil
|
26
|
+
@game_in_matchup = 3
|
27
|
+
end
|
28
|
+
|
29
|
+
def update_state
|
30
|
+
return if @champion_team
|
31
|
+
|
32
|
+
now = Time.at(Time.now.to_i)
|
33
|
+
five_sec_intervals = (now - @last_update_time).div(5)
|
34
|
+
|
35
|
+
return unless five_sec_intervals.positive?
|
36
|
+
|
37
|
+
five_sec_intervals.times do |i|
|
38
|
+
if ((i + @passed_updates) % 720).zero?
|
39
|
+
new_games
|
40
|
+
else
|
41
|
+
update_games
|
42
|
+
end
|
43
|
+
|
44
|
+
break if @champion_team
|
45
|
+
end
|
46
|
+
|
47
|
+
@last_update_time = now
|
48
|
+
@passed_updates += five_sec_intervals
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def new_games
|
54
|
+
if @game_in_matchup != (@day > 38 ? 5 : 3)
|
55
|
+
# New game in matchups
|
56
|
+
@games.map! { |game| Game.new(game.away, game.home, @prng) }
|
57
|
+
@game_in_matchup += 1
|
58
|
+
return
|
59
|
+
end
|
60
|
+
|
61
|
+
# New matchups
|
62
|
+
@games.clear
|
63
|
+
@game_in_matchup = 1
|
64
|
+
@day += 1
|
65
|
+
|
66
|
+
case @day <=> 39
|
67
|
+
when -1
|
68
|
+
(@shuffled_teams.length / 2).times do |i|
|
69
|
+
pair = [@shuffled_teams[i], @shuffled_teams[-i - 1]]
|
70
|
+
@games << Game.new(*(@day > 19 ? pair : pair.reverse), @prng)
|
71
|
+
end
|
72
|
+
|
73
|
+
@shuffled_teams.insert 1, @shuffled_teams.pop
|
74
|
+
when 0
|
75
|
+
@playoff_teams = sort_teams_by_wins(
|
76
|
+
@divisions.values.map do |teams|
|
77
|
+
sort_teams_by_wins(teams).first(2)
|
78
|
+
end.reduce(:+)
|
79
|
+
).map(&:clone)
|
80
|
+
|
81
|
+
new_playoff_matchups
|
82
|
+
when 1
|
83
|
+
@playoff_teams.select! { |team| team.wins > team.losses }
|
84
|
+
|
85
|
+
if @playoff_teams.length == 1
|
86
|
+
@champion_team = @playoff_teams[0]
|
87
|
+
return
|
88
|
+
end
|
89
|
+
|
90
|
+
new_playoff_matchups
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def update_games
|
95
|
+
@games_in_progress.each(&:update)
|
96
|
+
@games_in_progress = @games.select(&:in_progress)
|
97
|
+
@divisions.transform_values!(&method(:sort_teams_by_wins))
|
98
|
+
end
|
99
|
+
|
100
|
+
def new_playoff_matchups
|
101
|
+
@playoff_teams.each do |team|
|
102
|
+
team.wins = 0
|
103
|
+
team.losses = 0
|
104
|
+
end
|
105
|
+
|
106
|
+
(@playoff_teams.length / 2).times do |i|
|
107
|
+
@games << Game.new(@playoff_teams[i], @playoff_teams[-i - 1], @prng)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def sort_teams_by_wins(teams)
|
112
|
+
teams.sort { |a, b| b.wins <=> a.wins }
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require('hlockey/version')
|
4
|
+
|
5
|
+
module Hlockey
|
6
|
+
class Messages
|
7
|
+
private_class_method(:new)
|
8
|
+
|
9
|
+
def initialize(event, fields, data)
|
10
|
+
@event = event
|
11
|
+
fields.zip(data) do |(f, d)|
|
12
|
+
instance_variable_set("@#{f}", d)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class << self
|
17
|
+
# These are messages logged to game streams
|
18
|
+
|
19
|
+
[
|
20
|
+
%i[StartOfGame],
|
21
|
+
%i[EndOfGame winning_team],
|
22
|
+
%i[StartOfPeriod period],
|
23
|
+
%i[EndOfPeriod period home away home_score away_score],
|
24
|
+
%i[FaceOff winning_player new_puck_team],
|
25
|
+
%i[Hit puck_holder defender puck_taken new_puck_team],
|
26
|
+
%i[Pass sender receiver interceptor new_puck_team],
|
27
|
+
%i[ShootScore shooter home away home_score away_score],
|
28
|
+
%i[ShootBlock shooter blocker puck_taken new_puck_team]
|
29
|
+
].each do |event, *fields|
|
30
|
+
define_method(event) do |*data|
|
31
|
+
new(event, fields, data)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# These are messages used elsewhere
|
36
|
+
|
37
|
+
def SeasonDay(day)
|
38
|
+
"Season #{VERSION} day #{day}"
|
39
|
+
end
|
40
|
+
|
41
|
+
def SeasonStarts(time)
|
42
|
+
time.strftime("Season #{VERSION} starts at %H:%M, %A, %B %d (%Z).")
|
43
|
+
end
|
44
|
+
|
45
|
+
def SeasonChampion(team)
|
46
|
+
"Your season #{VERSION} champions are the #{team}!"
|
47
|
+
end
|
48
|
+
|
49
|
+
def NoGames
|
50
|
+
'no games right now. it is the offseason. join the Hlockey Discord for updates'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_s
|
55
|
+
case @event
|
56
|
+
when :StartOfGame
|
57
|
+
'Hocky!'
|
58
|
+
when :EndOfGame
|
59
|
+
"Game over.\n#{@winning_team} win!"
|
60
|
+
when :StartOfPeriod
|
61
|
+
"Start#{of_period}"
|
62
|
+
when :EndOfPeriod
|
63
|
+
"End#{of_period}#{score}"
|
64
|
+
when :FaceOff
|
65
|
+
"#{@winning_player} wins the faceoff!#{possession_change}"
|
66
|
+
when :Hit
|
67
|
+
"#{@defender} hits #{@puck_holder}#{takes}"
|
68
|
+
when :Pass
|
69
|
+
"#{@sender} passes to #{@receiver}." +
|
70
|
+
"#{"..\nIntercepted by #{@interceptor}!#{possession_change}" if @interceptor}"
|
71
|
+
when :ShootScore
|
72
|
+
"#{shot} and scores!#{score}"
|
73
|
+
when :ShootBlock
|
74
|
+
"#{shot}...\n#{@blocker} blocks the shot#{takes}"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def of_period
|
81
|
+
" of period #{@period}."
|
82
|
+
end
|
83
|
+
|
84
|
+
def score
|
85
|
+
"\n#{@home} #{@home_score}, #{@away} #{@away_score}"
|
86
|
+
end
|
87
|
+
|
88
|
+
def shot
|
89
|
+
"#{@shooter} takes a shot"
|
90
|
+
end
|
91
|
+
|
92
|
+
def takes
|
93
|
+
@puck_taken ? " and takes the puck!#{possession_change}" : '!'
|
94
|
+
end
|
95
|
+
|
96
|
+
def possession_change
|
97
|
+
"\n#{@new_puck_team} have possession."
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
data/lib/hlockey/team.rb
ADDED
@@ -0,0 +1,35 @@
|
|
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
|
data/lib/hlockey.rb
ADDED
metadata
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hlockey
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '
|
4
|
+
version: '2'
|
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-
|
11
|
+
date: 2022-08-08 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
|
-
description:
|
13
|
+
description: Hlockey library and console application.
|
14
14
|
email: endie2@protonmail.com
|
15
15
|
executables:
|
16
16
|
- hlockey
|
@@ -18,17 +18,22 @@ extensions: []
|
|
18
18
|
extra_rdoc_files: []
|
19
19
|
files:
|
20
20
|
- bin/hlockey
|
21
|
-
- lib/
|
22
|
-
- lib/
|
23
|
-
- lib/
|
24
|
-
- lib/
|
25
|
-
- lib/
|
26
|
-
- lib/
|
27
|
-
|
21
|
+
- lib/hlockey.rb
|
22
|
+
- 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
|
+
- lib/hlockey/game.rb
|
28
|
+
- lib/hlockey/league.rb
|
29
|
+
- lib/hlockey/messages.rb
|
30
|
+
- lib/hlockey/team.rb
|
31
|
+
- lib/hlockey/version.rb
|
32
|
+
homepage: https://hlockey.herokuapp.com
|
28
33
|
licenses:
|
29
34
|
- LicenseRef-LICENSE.md
|
30
35
|
metadata:
|
31
|
-
source_code_uri: https://github.com/
|
36
|
+
source_code_uri: https://github.com/Hlockey/hlockey
|
32
37
|
post_install_message:
|
33
38
|
rdoc_options: []
|
34
39
|
require_paths:
|
@@ -47,5 +52,5 @@ requirements: []
|
|
47
52
|
rubygems_version: 3.3.7
|
48
53
|
signing_key:
|
49
54
|
specification_version: 4
|
50
|
-
summary: Hlockey season
|
55
|
+
summary: Hlockey season 2.
|
51
56
|
test_files: []
|