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/game.rb
DELETED
@@ -1,153 +0,0 @@
|
|
1
|
-
require_relative "./messages"
|
2
|
-
|
3
|
-
class Game
|
4
|
-
attr_reader :stream, :in_progress
|
5
|
-
|
6
|
-
def initialize home, away, prng
|
7
|
-
@stream = [Messages.StartOfGame(to_s)]
|
8
|
-
@in_progress = true
|
9
|
-
@home, @away, @prng = [home, away, prng]
|
10
|
-
@score = {
|
11
|
-
:home => 0,
|
12
|
-
:away => 0
|
13
|
-
}
|
14
|
-
@actions = 0
|
15
|
-
@period = 1
|
16
|
-
@face_off = true
|
17
|
-
@team_with_puck = nil
|
18
|
-
@puckless_team = nil
|
19
|
-
@puck_holder = nil
|
20
|
-
@shooting_chance = 0
|
21
|
-
end
|
22
|
-
|
23
|
-
def to_s
|
24
|
-
puts "#{home.emoji} #{home.name} vs #{away.name} #{away.emoji}"
|
25
|
-
end
|
26
|
-
|
27
|
-
def update
|
28
|
-
return unless @in_progress
|
29
|
-
|
30
|
-
@stream << Messages.StartOfPeriod(@period) if @actions == 0
|
31
|
-
@actions += 1
|
32
|
-
|
33
|
-
if @face_off
|
34
|
-
if @puck_holder == nil
|
35
|
-
# Pass opposite team to who wins the puck to switch_team_with_puck,
|
36
|
-
# so @team_with_puck & @puckless_team are set to the correct values.
|
37
|
-
switch_team_with_puck(
|
38
|
-
action_succeeds?(@home.roster[:center].stats[:offense],
|
39
|
-
@away.roster[:center].stats[:offense]) ? @away : @home)
|
40
|
-
|
41
|
-
@puck_holder = @team_with_puck.roster[:center]
|
42
|
-
@stream << Messages.FaceOff(@puck_holder.name)
|
43
|
-
else
|
44
|
-
pass
|
45
|
-
@face_off = false
|
46
|
-
end
|
47
|
-
return
|
48
|
-
end
|
49
|
-
|
50
|
-
case @prng.rand 5 + @shooting_chance
|
51
|
-
when 0..4
|
52
|
-
pass
|
53
|
-
when 5..6 # Check
|
54
|
-
defender = puckless_non_goalie
|
55
|
-
@stream << Messages.Hit(@puck_holder.name, defender.name,
|
56
|
-
try_take_puck(defender), 0, :defense)
|
57
|
-
else # Shoot
|
58
|
-
unless @shooting_chance < 5 and
|
59
|
-
try_block_shot @puckless_team.roster[@prng.rand(2) == 0 ? :ldef : :rdef] or
|
60
|
-
try_block_shot @puckless_team.roster[:goalie]
|
61
|
-
@score[@team_with_puck == @home ? :home : :away] += 1
|
62
|
-
|
63
|
-
@stream << Messages.Shoot(shooter,
|
64
|
-
nil, false,
|
65
|
-
@home.name, @away.name,
|
66
|
-
*@score.values)
|
67
|
-
|
68
|
-
@shooting_chance = 0
|
69
|
-
@face_off = true
|
70
|
-
@actions = 60 if @period > 3 # Sudden death overtime
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
if @actions == 60
|
75
|
-
@stream << Messages.EndOfPeriod(@period, @home.name, @away.name, *@score.values)
|
76
|
-
@actions = 0
|
77
|
-
@period += 1
|
78
|
-
if @period > 3 and not(@score[:home] == @score[:away] and @period < 12)
|
79
|
-
# Game is over
|
80
|
-
@in_progress = false
|
81
|
-
|
82
|
-
winner, loser = case @score[:home] <=> @score[:away]
|
83
|
-
when 1
|
84
|
-
[@home, @away]
|
85
|
-
when -1
|
86
|
-
[@away, @home]
|
87
|
-
else # Tie
|
88
|
-
@stream << Messages.EndOfGame
|
89
|
-
@home.losses += 1
|
90
|
-
@away.losses += 1
|
91
|
-
return
|
92
|
-
end
|
93
|
-
|
94
|
-
@stream << Messages.EndOfGame(winner.name)
|
95
|
-
winner.wins += 1
|
96
|
-
loser.losses += 1
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
private
|
102
|
-
def action_succeeds? helping_stat, hindering_stat
|
103
|
-
@prng.rand(10) + helping_stat - hindering_stat > 4
|
104
|
-
end
|
105
|
-
|
106
|
-
def switch_team_with_puck team_with_puck = @team_with_puck
|
107
|
-
@team_with_puck, @puckless_team = team_with_puck == @home ?
|
108
|
-
[@away, @home] : [@home, @away]
|
109
|
-
@shooting_chance = 0
|
110
|
-
end
|
111
|
-
|
112
|
-
def pass
|
113
|
-
sender_name = @puck_holder.name
|
114
|
-
receiver = puckless_non_goalie(@team_with_puck)
|
115
|
-
interceptor = puckless_non_goalie
|
116
|
-
|
117
|
-
if not @face_off and try_take_puck interceptor, 2 # Pass intercepted
|
118
|
-
@stream << Message.Pass(sender_name, receiver.name, interceptor)
|
119
|
-
return
|
120
|
-
end
|
121
|
-
|
122
|
-
@stream << Message.Pass(sender_name, receiver.name)
|
123
|
-
@puck_holder = receiver
|
124
|
-
@shooting_chance += 1
|
125
|
-
end
|
126
|
-
|
127
|
-
def try_take_puck player, dis = 0, stat = :agility
|
128
|
-
return false unless action_succeeds?(player.stats[stat] - dis,
|
129
|
-
@puck_holder.stats[stat])
|
130
|
-
|
131
|
-
switch_team_with_puck
|
132
|
-
@puck_holder = player
|
133
|
-
|
134
|
-
true
|
135
|
-
end
|
136
|
-
|
137
|
-
def try_block_shot blocker
|
138
|
-
return false unless action_succeeds?(blocker.stats[:defense],
|
139
|
-
@puck_holder.stats[:offense])
|
140
|
-
|
141
|
-
@shooting_chance += 1
|
142
|
-
@stream << Message.Shoot(@puck_holder, blocker, try_take_puck(blocker))
|
143
|
-
|
144
|
-
true
|
145
|
-
end
|
146
|
-
|
147
|
-
def puckless_non_goalie team = @puckless_team
|
148
|
-
team.roster.values.select do |p|
|
149
|
-
p != team.roster[:goalie] and p != @puck_holder
|
150
|
-
end.sample random: @prng
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
data/lib/league.rb
DELETED
@@ -1,130 +0,0 @@
|
|
1
|
-
require_relative "./game"
|
2
|
-
require_relative "./team"
|
3
|
-
|
4
|
-
class League
|
5
|
-
attr_reader :day, :divisions, :games_in_progress, :playoff_teams, :champion_team
|
6
|
-
|
7
|
-
def initialize season, start_time
|
8
|
-
@day = 0
|
9
|
-
@divisions = {
|
10
|
-
"Wet Warm": [
|
11
|
-
Team.new("Antalya Pirates", "🌊"),
|
12
|
-
Team.new("Baden Hallucinations", "🍄"),
|
13
|
-
Team.new("Kópavogur Seals", "🦭"),
|
14
|
-
Team.new("Lagos Soup", "🥣"),
|
15
|
-
Team.new("Pica Acid", "🧪")
|
16
|
-
],
|
17
|
-
"Dry Warm": [
|
18
|
-
Team.new("Dawson City Impostors", "🔪"),
|
19
|
-
Team.new("Erlangen Ohms", "🇴"),
|
20
|
-
Team.new("Pompei Eruptions", "🌋"),
|
21
|
-
Team.new("Rio de Janeiro Directors", "🎦"),
|
22
|
-
Team.new("Wyrzysk Rockets", "🚀")
|
23
|
-
],
|
24
|
-
"Wet Cool": [
|
25
|
-
Team.new("Cape Town Transplants", "🌱"),
|
26
|
-
Team.new("Manbij Fish", "🐠"),
|
27
|
-
Team.new("Nagqu Paint", "🎨"),
|
28
|
-
Team.new("Nice Backflippers", "🔄"),
|
29
|
-
Team.new("Orcadas Base Fog", "🌁")
|
30
|
-
],
|
31
|
-
"Dry Cool": [
|
32
|
-
Team.new("Baghdad Abacuses", "🧮"),
|
33
|
-
Team.new("Jakarta Architects", "📐"),
|
34
|
-
Team.new("Kyoto Payphones", "📳"),
|
35
|
-
Team.new("Stony Brook Reapers", "💀"),
|
36
|
-
Team.new("Sydney Thinkers", "🤔")
|
37
|
-
]
|
38
|
-
}
|
39
|
-
@games_in_progress = []
|
40
|
-
@games = []
|
41
|
-
@champion_team = nil
|
42
|
-
@last_update_time = start_time
|
43
|
-
@passed_updates = 0
|
44
|
-
@prng = Random.new 69420 * season
|
45
|
-
@shuffled_teams = @divisions.values.reduce(:+).shuffle random: @prng
|
46
|
-
@playoff_teams = nil
|
47
|
-
@game_in_matchup = 3
|
48
|
-
end
|
49
|
-
|
50
|
-
def update_state
|
51
|
-
return if @champion_team
|
52
|
-
|
53
|
-
now = Time.now
|
54
|
-
five_sec_intervals = (now - @last_update_time).floor / 5
|
55
|
-
|
56
|
-
if five_sec_intervals > 0
|
57
|
-
five_sec_intervals.times do |i|
|
58
|
-
if (i + @passed_updates) % 720 == 0
|
59
|
-
new_games
|
60
|
-
else
|
61
|
-
update_games
|
62
|
-
end
|
63
|
-
|
64
|
-
return if @champion_team
|
65
|
-
end
|
66
|
-
|
67
|
-
@last_update_time = now
|
68
|
-
@passed_updates += five_sec_intervals
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
private
|
73
|
-
def new_games
|
74
|
-
if @game_in_matchup != (@day > 38 ? 5 : 3)
|
75
|
-
# New game in matchups
|
76
|
-
@games.map! do |game| Game.new(game.away, game.home, @prng) end
|
77
|
-
@game_in_matchup += 1
|
78
|
-
return
|
79
|
-
end
|
80
|
-
|
81
|
-
# New matchups
|
82
|
-
@games.clear
|
83
|
-
@game_in_matchup = 1
|
84
|
-
@day += 1
|
85
|
-
|
86
|
-
case @day <=> 39
|
87
|
-
when -1
|
88
|
-
(@shuffled_teams.length / 2).times do |i|
|
89
|
-
pair = [@shuffled_teams[i], @shuffled_teams[-i]]
|
90
|
-
@games << Game.new(*(@day > 19 ? pair : pair.reverse), @prng)
|
91
|
-
end
|
92
|
-
|
93
|
-
@shuffled_teams.insert 1, @shuffled_teams.pop
|
94
|
-
when 0
|
95
|
-
@playoff_teams = Team.sort_teams(
|
96
|
-
@divisions.values.map do |teams|
|
97
|
-
Team.sort_teams(teams).first(2)
|
98
|
-
end.reduce(:+).map &:copy
|
99
|
-
)
|
100
|
-
|
101
|
-
new_playoff_matchups
|
102
|
-
when 1
|
103
|
-
@playoff_teams = Team.sort_teams(@playoff_teams).first(@playoff_teams.length / 2)
|
104
|
-
|
105
|
-
if @playoff_teams.length == 1
|
106
|
-
@champion_team = @playoff_teams[0]
|
107
|
-
return
|
108
|
-
end
|
109
|
-
|
110
|
-
new_playoff_matchups
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
def update_games
|
115
|
-
@games_in_progress = @games.select do |game| game.in_progress end
|
116
|
-
@games_in_progress.each do |game| game.update end
|
117
|
-
end
|
118
|
-
|
119
|
-
def new_playoff_matchups
|
120
|
-
@playoff_teams.each do |team|
|
121
|
-
team.wins = 0
|
122
|
-
team.losses = 0
|
123
|
-
end
|
124
|
-
|
125
|
-
(0...@playoff_teams.length).step 2 do |i|
|
126
|
-
@games << Game.new(*@playoff_teams[i, 2], @prng)
|
127
|
-
end
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
data/lib/messages.rb
DELETED
@@ -1,64 +0,0 @@
|
|
1
|
-
class Messages
|
2
|
-
def initialize event, fields, data
|
3
|
-
@event = event
|
4
|
-
data_fields.zip data do |(f, d)|
|
5
|
-
instance_variable_set "@" + f, d
|
6
|
-
end
|
7
|
-
end
|
8
|
-
|
9
|
-
private_class_method :new
|
10
|
-
|
11
|
-
[
|
12
|
-
[:StartOfGame, :title],
|
13
|
-
[:EndOfGame, :winning_team],
|
14
|
-
[:StartOfPeriod, :period],
|
15
|
-
[:EndOfPeriod, :period, :home, :away, :home_score, :away_score],
|
16
|
-
[:FaceOff, :winning_player],
|
17
|
-
[:Hit, :puck_holder, :defender, :puck_taken],
|
18
|
-
[:Pass, :sender, :receiver, :interceptor],
|
19
|
-
[:Shoot, :shooter, :blocker, :puck_taken, :home, :away, :home_score, :away_score]
|
20
|
-
].each do |event, *fields|
|
21
|
-
define_method event do |*data|
|
22
|
-
new event, fields, data
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def to_s
|
27
|
-
case @event
|
28
|
-
when :StartOfGame
|
29
|
-
@title + "\nHocky!"
|
30
|
-
when :EndOfGame
|
31
|
-
"Game over.\n#{@winning_team or "Nobody"} wins!"
|
32
|
-
when :StartOfPeriod
|
33
|
-
"Start" + of_period
|
34
|
-
when :EndOfPeriod
|
35
|
-
"End" + of_period + score
|
36
|
-
when :FaceOff
|
37
|
-
@winning_player + " wins the faceoff!"
|
38
|
-
when :Hit
|
39
|
-
"#{@defender} hits #{@puck_holder + takes}!"
|
40
|
-
when :Pass
|
41
|
-
"#{@sender} passes to #{@receiver}#{@interceptor ?
|
42
|
-
"... intercepted by #{@interceptor}!" : "."}"
|
43
|
-
when :Shoot
|
44
|
-
"#{@shooter} takes a shot... #{@blocker ?
|
45
|
-
"#{@blocker} blocks the shot#{takes}!" : "and scores!" + score}"
|
46
|
-
else
|
47
|
-
raise "Unknown message"
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
private
|
52
|
-
def of_period
|
53
|
-
" of period #{@period}."
|
54
|
-
end
|
55
|
-
|
56
|
-
def score
|
57
|
-
"\n#{@home} #{@home_score}, #{@away} #{@away_score}"
|
58
|
-
end
|
59
|
-
|
60
|
-
def takes
|
61
|
-
@puck_taken ? " and takes the puck" : ""
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
data/lib/player.rb
DELETED
@@ -1,41 +0,0 @@
|
|
1
|
-
require_relative "./data/names_markov_chain"
|
2
|
-
|
3
|
-
class Player
|
4
|
-
attr_reader :name, :stats
|
5
|
-
|
6
|
-
@@prng = Random.new 8010897121101114 # This never changes
|
7
|
-
|
8
|
-
def initialize
|
9
|
-
@name = random_name + " " + random_name
|
10
|
-
@stats = [:offense, :defense, :agility].each_with_object Hash.new do |stat, hash|
|
11
|
-
hash[stat] = @@prng.rand * 5
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
private
|
16
|
-
def random_name
|
17
|
-
combination = "__"
|
18
|
-
next_letter = ""
|
19
|
-
result = ""
|
20
|
-
|
21
|
-
10.times do
|
22
|
-
next_letters = NamesMarkovChain[combination]
|
23
|
-
break if next_letters.nil?
|
24
|
-
|
25
|
-
cumulative_weights = []
|
26
|
-
next_letters.values.each do |v|
|
27
|
-
cumulative_weights << v + (cumulative_weights.last or 0)
|
28
|
-
end
|
29
|
-
rand_num = @@prng.rand cumulative_weights.last
|
30
|
-
|
31
|
-
next_letter = next_letters.keys[cumulative_weights.index do |n| rand_num < n end]
|
32
|
-
break if next_letter == "_"
|
33
|
-
|
34
|
-
result += next_letter
|
35
|
-
combination = combination[1] + next_letter
|
36
|
-
end
|
37
|
-
|
38
|
-
result
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
data/lib/team.rb
DELETED
@@ -1,46 +0,0 @@
|
|
1
|
-
require_relative "./player"
|
2
|
-
|
3
|
-
class Team
|
4
|
-
attr_accessor :wins, :losses
|
5
|
-
attr_reader :name, :emoji, :roster
|
6
|
-
|
7
|
-
def initialize name, emoji, roster_idxs = {:lwing => 0,
|
8
|
-
:center => 1,
|
9
|
-
:rwing => 2,
|
10
|
-
:ldef => 3,
|
11
|
-
:goalie => 4,
|
12
|
-
:rdef => 5}
|
13
|
-
@wins = 0
|
14
|
-
@losses = 0
|
15
|
-
@name, @emoji = [name, emoji]
|
16
|
-
@roster = {}
|
17
|
-
players = []
|
18
|
-
6.times do players << Player.new end
|
19
|
-
roster_idxs.each do |pos, i|
|
20
|
-
@roster[pos] = players[i]
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def to_s
|
25
|
-
"#{@emoji} #{@name}"
|
26
|
-
end
|
27
|
-
|
28
|
-
def print_win_loss
|
29
|
-
puts " #{to_s.ljust 26} #{@wins}-#{@losses}"
|
30
|
-
end
|
31
|
-
|
32
|
-
def print_roster
|
33
|
-
puts to_s
|
34
|
-
@roster.each do |pos, player|
|
35
|
-
puts " #{pos.to_s.ljust 6}: #{player.name}"
|
36
|
-
player.stats.each do |stat, value|
|
37
|
-
puts " #{stat}: #{value.round 1}"
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
def self.sort_teams teams
|
43
|
-
teams.sort do |a, b| a.wins - a.losses <=> b.wins - b.losses end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|