hlockey 2 → 4
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 +21 -0
- data/data/external/names.txt +19948 -0
- data/data/information.yaml +24 -0
- data/data/league.yaml +1526 -0
- data/data/links.yaml +3 -0
- data/data/previous_election_results.yaml +59 -0
- data/lib/hlockey/constants.rb +4 -0
- data/lib/hlockey/data.rb +17 -13
- data/lib/hlockey/game/actions.rb +27 -0
- data/lib/hlockey/game/fight.rb +99 -0
- data/lib/hlockey/game.rb +321 -159
- data/lib/hlockey/league.rb +254 -115
- data/lib/hlockey/message.rb +182 -0
- data/lib/hlockey/player.rb +73 -0
- data/lib/hlockey/team.rb +52 -35
- data/lib/hlockey/utils.rb +15 -0
- data/lib/hlockey/version.rb +3 -5
- data/lib/hlockey/weather.rb +168 -0
- data/lib/hlockey.rb +1 -3
- metadata +36 -16
- data/bin/hlockey +0 -104
- data/lib/hlockey/data/election.yaml +0 -16
- data/lib/hlockey/data/information.yaml +0 -12
- data/lib/hlockey/data/league.yaml +0 -846
- data/lib/hlockey/data/links.yaml +0 -2
- data/lib/hlockey/messages.rb +0 -100
data/lib/hlockey/league.rb
CHANGED
@@ -1,115 +1,254 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require(
|
4
|
-
require(
|
5
|
-
require(
|
6
|
-
require(
|
7
|
-
|
8
|
-
module Hlockey
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
@
|
48
|
-
@
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
end
|
1
|
+
require("hlockey/constants")
|
2
|
+
require("hlockey/data")
|
3
|
+
require("hlockey/game")
|
4
|
+
require("hlockey/utils")
|
5
|
+
require("hlockey/version")
|
6
|
+
require("hlockey/weather")
|
7
|
+
|
8
|
+
module Hlockey
|
9
|
+
##
|
10
|
+
# The Hlockey League
|
11
|
+
class League
|
12
|
+
GAMES_IN_REGULAR_SEASON = 114
|
13
|
+
|
14
|
+
# @return [Time]
|
15
|
+
attr_reader(:start_time)
|
16
|
+
|
17
|
+
# @return [Hash<Symbol => Array<Team>>]
|
18
|
+
attr_reader(:divisions)
|
19
|
+
|
20
|
+
# @return [Integer]
|
21
|
+
attr_reader(:day)
|
22
|
+
|
23
|
+
# @return [Array<Game>]
|
24
|
+
attr_reader(:games, :games_in_progress)
|
25
|
+
|
26
|
+
# @return [Array<String>]
|
27
|
+
attr_reader(:alerts)
|
28
|
+
|
29
|
+
# @return [Team, nil]
|
30
|
+
attr_reader(:champion_team)
|
31
|
+
|
32
|
+
# @return [Array<Team>]
|
33
|
+
attr_reader(:teams, :playoff_teams)
|
34
|
+
|
35
|
+
def initialize
|
36
|
+
@start_time, @divisions = Data.league
|
37
|
+
@start_time.localtime
|
38
|
+
@day = 0
|
39
|
+
@games = []
|
40
|
+
@games_in_progress = []
|
41
|
+
@alerts = []
|
42
|
+
@champion_team = nil
|
43
|
+
@last_update_time = @start_time
|
44
|
+
@passed_updates = 0
|
45
|
+
@prng = Random.new(@start_time.to_i)
|
46
|
+
@teams = @divisions.values.flatten
|
47
|
+
@sorted_teams_by_wins = @teams.shuffle(random: @prng)
|
48
|
+
@playoff_teams = []
|
49
|
+
# warm vs warm / cool vs cool
|
50
|
+
rotated_divisions = game_divisions.values.rotate
|
51
|
+
@shuffled_division_pairs = Array.new(2) do |i|
|
52
|
+
(rotated_divisions[i] + rotated_divisions[-i - 1]).shuffle(random: @prng)
|
53
|
+
end
|
54
|
+
|
55
|
+
@game_in_matchup = 3
|
56
|
+
@matchup_game_amt = 3
|
57
|
+
end
|
58
|
+
|
59
|
+
##
|
60
|
+
# Updates the league to the current state
|
61
|
+
# This should be called whenever you need the current state of the league
|
62
|
+
def update_state
|
63
|
+
return if @champion_team
|
64
|
+
|
65
|
+
now = Time.at(Time.now.to_i)
|
66
|
+
intervals = (now - @last_update_time).div(UPDATE_FREQUENCY_SECONDS)
|
67
|
+
|
68
|
+
return unless intervals.positive?
|
69
|
+
|
70
|
+
intervals.times do |i|
|
71
|
+
if ((i + @passed_updates) % UPDATES_PER_HOUR).zero?
|
72
|
+
update_teams
|
73
|
+
new_games
|
74
|
+
else
|
75
|
+
update_games
|
76
|
+
end
|
77
|
+
|
78
|
+
break if @champion_team
|
79
|
+
end
|
80
|
+
|
81
|
+
@last_update_time = now
|
82
|
+
@passed_updates += intervals
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
def update_teams
|
88
|
+
return if @day.zero?
|
89
|
+
|
90
|
+
# Boost Sleepers roster
|
91
|
+
unless @day > 41 # Unless Sleepers are playing
|
92
|
+
@teams.last.roster.each_value do |player|
|
93
|
+
stat_change_amount = (10 - player.stats.values.reduce(:+)) / 129
|
94
|
+
player.stats.transform_values! { |stat| stat + stat_change_amount }
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Update team statuses
|
99
|
+
games_remaining = GAMES_IN_REGULAR_SEASON - ((@day - 1) * 3 + @game_in_matchup)
|
100
|
+
return if games_remaining.negative?
|
101
|
+
|
102
|
+
if games_remaining.zero?
|
103
|
+
@playoff_teams = sort_teams_by_wins(playoff_qualifiers)
|
104
|
+
@playoff_teams.each { |team| team.status = :qualified }
|
105
|
+
(@teams[...-1] - @playoff_teams).each { |team| team.status = :partying }
|
106
|
+
return
|
107
|
+
end
|
108
|
+
|
109
|
+
set_to_qualify = playoff_qualifiers
|
110
|
+
@teams[...-1].each_with_index do |team, i|
|
111
|
+
next unless games_remaining < set_to_qualify[i / 5].wins - team.wins &&
|
112
|
+
games_remaining < set_to_qualify.last.wins - team.wins
|
113
|
+
|
114
|
+
team.status = :partying
|
115
|
+
end
|
116
|
+
|
117
|
+
threat_wins = sort_teams_by_wins(@teams - set_to_qualify).first.wins
|
118
|
+
set_to_qualify.each_with_index do |team, i|
|
119
|
+
division_threat_wins = if i < 4
|
120
|
+
sort_teams_by_wins(@divisions.values[i])[1].wins
|
121
|
+
else # Team is not a division leader, so use itself
|
122
|
+
team.wins
|
123
|
+
end
|
124
|
+
next unless games_remaining < team.wins - threat_wins ||
|
125
|
+
games_remaining < team.wins - division_threat_wins
|
126
|
+
|
127
|
+
team.status = :qualified
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def new_games
|
132
|
+
if @game_in_matchup != @matchup_game_amt
|
133
|
+
# New game in matchups
|
134
|
+
@games.map! { |game| new_game(game.away, game.home) }
|
135
|
+
@game_in_matchup += 1
|
136
|
+
return
|
137
|
+
end
|
138
|
+
|
139
|
+
# New matchups
|
140
|
+
@games.clear
|
141
|
+
@game_in_matchup = 1
|
142
|
+
@day += 1
|
143
|
+
|
144
|
+
case @day
|
145
|
+
when 1..27
|
146
|
+
@shuffled_division_pairs.each { |p| new_matchups(p, reverse: @day.even?) }
|
147
|
+
when 28..38
|
148
|
+
@shuffled_division_pairs.transpose.each_with_index do |pair, i|
|
149
|
+
@games << new_game(*pair, reverse: i.even?)
|
150
|
+
end
|
151
|
+
@shuffled_division_pairs.first.rotate!
|
152
|
+
when 39
|
153
|
+
@matchup_game_amt = 5
|
154
|
+
@playoff_teams = sort_teams_by_wins(playoff_qualifiers).map do |team|
|
155
|
+
cloned_team = team.clone
|
156
|
+
cloned_team.status = :playoffs
|
157
|
+
cloned_team
|
158
|
+
end
|
159
|
+
new_playoff_matchups
|
160
|
+
when 40, 41
|
161
|
+
@playoff_teams.select! { |team| team.wins > team.losses }
|
162
|
+
new_playoff_matchups
|
163
|
+
when 42
|
164
|
+
@matchup_game_amt = 1
|
165
|
+
champion = @playoff_teams.find { |team| team.wins > team.losses }
|
166
|
+
champion.wins = 0
|
167
|
+
champion.losses = 0
|
168
|
+
@playoff_teams = [@teams.last, champion]
|
169
|
+
@alerts = ["The Sleepers have awoken..."]
|
170
|
+
@games = [Game.new(*@playoff_teams, @prng, Stars)]
|
171
|
+
when 43, 44
|
172
|
+
@games = [Game.new(*@playoff_teams, @prng, Stars)]
|
173
|
+
when 45
|
174
|
+
return unless @champion_team.nil?
|
175
|
+
|
176
|
+
sleepers, @champion_team = @playoff_teams
|
177
|
+
@alerts = []
|
178
|
+
|
179
|
+
if sleepers.wins > @champion_team.wins
|
180
|
+
@alerts << "The Sleepers have beat your champions."
|
181
|
+
@alerts << "The Sleepers are evolving!"
|
182
|
+
sleepers.to_s = "Sleepersz"
|
183
|
+
@alerts << "Sleepers -> Sleepersz"
|
184
|
+
return
|
185
|
+
end
|
186
|
+
|
187
|
+
@alerts << "The Sleepers have lost."
|
188
|
+
sleepers.roster.values.zip([18, 4, 13, 19, 6, 0]) do |player, team_idx|
|
189
|
+
player.team = @teams[team_idx]
|
190
|
+
player.team.shadows << player
|
191
|
+
@alerts << "#{player} has adventured into the #{player.team} shadows."
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def update_games
|
197
|
+
@games_in_progress.each(&:update)
|
198
|
+
@games_in_progress = @games.select(&:in_progress)
|
199
|
+
@divisions.transform_values!(&method(:sort_teams_by_wins))
|
200
|
+
end
|
201
|
+
|
202
|
+
def new_playoff_matchups
|
203
|
+
@playoff_teams.each do |team|
|
204
|
+
team.wins = 0
|
205
|
+
team.losses = 0
|
206
|
+
end
|
207
|
+
|
208
|
+
new_matchups(@playoff_teams, rotate: false)
|
209
|
+
end
|
210
|
+
|
211
|
+
# @param matchup_teams[Array<Team>]
|
212
|
+
# @param rotate [Boolean]
|
213
|
+
# @param reverse [Boolean]
|
214
|
+
def new_matchups(matchup_teams, rotate: true, reverse: false)
|
215
|
+
(matchup_teams.length / 2).times do |i|
|
216
|
+
@games << new_game(matchup_teams[i], matchup_teams[-i - 1], reverse: reverse)
|
217
|
+
end
|
218
|
+
matchup_teams.insert(1, matchup_teams.pop) if rotate
|
219
|
+
end
|
220
|
+
|
221
|
+
# @param home [Team]
|
222
|
+
# @param away [Team]
|
223
|
+
# @param reverse [Boolean]
|
224
|
+
def new_game(home, away, reverse: false)
|
225
|
+
teams = [home, away]
|
226
|
+
teams.reverse! if reverse
|
227
|
+
|
228
|
+
Game.new(*teams, @prng, Weather::WEATHERS.sample(random: @prng))
|
229
|
+
end
|
230
|
+
|
231
|
+
# @return [Array<Team>]
|
232
|
+
def playoff_qualifiers
|
233
|
+
top_divs = game_divisions.values.map { |teams| sort_teams_by_wins(teams).first }
|
234
|
+
# Prevents tied teams from being unfairly decided by which division they're in
|
235
|
+
@sorted_teams_by_wins = sort_teams_by_wins(@sorted_teams_by_wins)
|
236
|
+
rest = (@sorted_teams_by_wins - top_divs).first(4)
|
237
|
+
|
238
|
+
top_divs + rest
|
239
|
+
end
|
240
|
+
|
241
|
+
# @param teams [Array<Team>]
|
242
|
+
# @return [Array<Team>]
|
243
|
+
def sort_teams_by_wins(teams)
|
244
|
+
# A stable sort (Ruby's normal sort isn't always stable)
|
245
|
+
# t.wins is negative because it sorts the opposite way than I want otherwise
|
246
|
+
teams.sort_by.with_index { |t, i| [-t.wins, i] }
|
247
|
+
end
|
248
|
+
|
249
|
+
# @return [Hash<Symbol => Array<Team>]
|
250
|
+
def game_divisions
|
251
|
+
@divisions.except(:"Sleepy Tired")
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
@@ -0,0 +1,182 @@
|
|
1
|
+
require("hlockey/version")
|
2
|
+
|
3
|
+
module Hlockey
|
4
|
+
##
|
5
|
+
# Can be sent by a Game (in Game#stream) or a commonly used text
|
6
|
+
class Message
|
7
|
+
private_class_method(:new)
|
8
|
+
|
9
|
+
@colorize = ->(_color, string) { string }
|
10
|
+
|
11
|
+
# @return [Symbol] an action or event the message represents
|
12
|
+
attr_reader(:event)
|
13
|
+
|
14
|
+
# @param event [Symbol]
|
15
|
+
# @param fields [Array<Symbol>] fields used in string interpolation in #to_s
|
16
|
+
# @param data [Array<Object>] data corresponding to the fields
|
17
|
+
def initialize(event, fields, data)
|
18
|
+
self.class.attr_reader(*fields)
|
19
|
+
|
20
|
+
@event = event
|
21
|
+
fields.zip(data) { |f, d| instance_variable_set("@#{f}", d) }
|
22
|
+
end
|
23
|
+
|
24
|
+
class << self
|
25
|
+
# Variable/method controlling how coloring players/teams is done
|
26
|
+
# The default is to not color things at all
|
27
|
+
|
28
|
+
attr_writer(:colorize)
|
29
|
+
|
30
|
+
# @param obj [Team, Player]
|
31
|
+
# @return String
|
32
|
+
def color(obj)
|
33
|
+
col = obj.instance_of?(Team) ? obj.color : obj.team.color
|
34
|
+
|
35
|
+
@colorize.call(col, obj.to_s)
|
36
|
+
end
|
37
|
+
|
38
|
+
# These are messages logged to game streams
|
39
|
+
[
|
40
|
+
%i[StartOfGame],
|
41
|
+
%i[EndOfGame winning_team],
|
42
|
+
%i[StartOfPeriod period],
|
43
|
+
%i[EndOfPeriod period home away home_score away_score],
|
44
|
+
%i[FaceOff winning_player new_puck_team],
|
45
|
+
%i[Hit puck_holder defender puck_taken new_puck_team shooting_chance],
|
46
|
+
%i[Pass sender receiver shooting_chance interceptor new_puck_team],
|
47
|
+
%i[ShootScore shooter home away home_score away_score audacity],
|
48
|
+
%i[ShootBlock shooter blocker puck_taken new_puck_team shooting_chance audacity],
|
49
|
+
%i[Partying player],
|
50
|
+
%i[FightStarted home_player away_player],
|
51
|
+
%i[FightAttack attacking_player defending_player blocked],
|
52
|
+
%i[PlayerJoinedFight team player],
|
53
|
+
%i[FightEnded],
|
54
|
+
%i[MoraleChange team amount],
|
55
|
+
%i[ChickenedOut prev_player next_player],
|
56
|
+
%i[InclineFavors team],
|
57
|
+
%i[StarsAlign team],
|
58
|
+
%i[WavesWashedAway prev_player next_player]
|
59
|
+
].each do |event, *fields|
|
60
|
+
define_method(event) { |*data| new(event, fields, data) }
|
61
|
+
end
|
62
|
+
|
63
|
+
# These are messages used elsewhere
|
64
|
+
|
65
|
+
def SeasonDay(day)
|
66
|
+
"Season #{VERSION} day #{day}"
|
67
|
+
end
|
68
|
+
|
69
|
+
def SeasonStarts(time)
|
70
|
+
time.strftime("Season #{VERSION} starts at %H:%M, %A, %B %d (%Z).")
|
71
|
+
end
|
72
|
+
|
73
|
+
def SeasonChampion(team)
|
74
|
+
"Your season #{VERSION} champions are the #{team}!"
|
75
|
+
end
|
76
|
+
|
77
|
+
def NoGames
|
78
|
+
"no games right now. it is the offseason. join the Hlockey Discord for updates"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def to_s
|
83
|
+
case @event
|
84
|
+
when :StartOfGame
|
85
|
+
"Hocky!"
|
86
|
+
when :EndOfGame
|
87
|
+
"Game over.\n#{color(@winning_team)} win!"
|
88
|
+
when :StartOfPeriod
|
89
|
+
"Start#{of_period}"
|
90
|
+
when :EndOfPeriod
|
91
|
+
"End#{of_period}#{score}"
|
92
|
+
when :FaceOff
|
93
|
+
"#{color(@winning_player)} wins the faceoff!#{possession_change}"
|
94
|
+
when :Hit
|
95
|
+
str = "#{color(@defender)} hits #{color(@puck_holder)}"
|
96
|
+
str += takes + score_chance if @puck_taken
|
97
|
+
str
|
98
|
+
when :Pass
|
99
|
+
str = "#{color(@sender)} passes to #{color(@receiver)}."
|
100
|
+
unless @interceptor.nil?
|
101
|
+
str += "..\nIntercepted by #{color(@interceptor)}!#{possession_change}"
|
102
|
+
end
|
103
|
+
str += score_chance
|
104
|
+
str
|
105
|
+
when :ShootScore
|
106
|
+
"#{shot} and scores!#{score}"
|
107
|
+
when :ShootBlock
|
108
|
+
"#{shot}...\n#{color(@blocker)} blocks the shot#{takes}#{score_chance}"
|
109
|
+
when :Partying
|
110
|
+
"#{color(@player)} is partying!"
|
111
|
+
when :FightStarted
|
112
|
+
"#{color(@home_player)} and #{color(@away_player)} start fighting!"
|
113
|
+
when :FightAttack
|
114
|
+
str = "#{color(@attacking_player)} punches #{color(@defending_player)}!"
|
115
|
+
str += "\n#{color(@defending_player)} blocks the punch!" if @blocked
|
116
|
+
str
|
117
|
+
when :PlayerJoinedFight
|
118
|
+
"#{color(@player)} from #{color(@team)} joins the fight!"
|
119
|
+
when :FightEnded
|
120
|
+
"The fight has ended."
|
121
|
+
when :MoraleChange
|
122
|
+
"#{color(@team)} #{@amount.negative? ? "loses" : "gains"} #{@amount.abs} morale."
|
123
|
+
when :ChickenedOut
|
124
|
+
"#{color(@prev_player)} chickened out!#{replaces("game")}"
|
125
|
+
when :InclineFavors
|
126
|
+
"The incline favors #{color(@team)}."
|
127
|
+
when :StarsAlign
|
128
|
+
"The stars align for #{color(@team)}. They get half a goal."
|
129
|
+
when :WavesWashedAway
|
130
|
+
"#{color(@prev_player)} is washed away by the waves...#{replaces}"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
private
|
135
|
+
|
136
|
+
def color(obj)
|
137
|
+
self.class.color(obj)
|
138
|
+
end
|
139
|
+
|
140
|
+
def score_chance
|
141
|
+
return "" if @shooting_chance.nil?
|
142
|
+
|
143
|
+
chance_str = case @shooting_chance
|
144
|
+
when 0..2
|
145
|
+
"none"
|
146
|
+
when 3, 4
|
147
|
+
"low"
|
148
|
+
when 5, 6
|
149
|
+
"medium"
|
150
|
+
else
|
151
|
+
"high"
|
152
|
+
end
|
153
|
+
"\nChance of scoring: #{chance_str} (#{@shooting_chance})"
|
154
|
+
end
|
155
|
+
|
156
|
+
def of_period
|
157
|
+
" of period #{@period}."
|
158
|
+
end
|
159
|
+
|
160
|
+
def score
|
161
|
+
"\n#{color(@home)} #{@home_score.round(2)}, #{color(@away)} #{@away_score.round(2)}"
|
162
|
+
end
|
163
|
+
|
164
|
+
def shot
|
165
|
+
"#{color(@shooter)} takes a#{"n audacious" if @audacity} shot"
|
166
|
+
end
|
167
|
+
|
168
|
+
def takes
|
169
|
+
@puck_taken ? " and takes the puck!#{possession_change}" : "!"
|
170
|
+
end
|
171
|
+
|
172
|
+
def possession_change
|
173
|
+
"\n#{color(@new_puck_team)} have possession."
|
174
|
+
end
|
175
|
+
|
176
|
+
def replaces(period = nil)
|
177
|
+
str = "\n#{color(@next_player)} replaces them"
|
178
|
+
str += " for the rest of the #{period}." unless period.nil?
|
179
|
+
str
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require("hlockey/utils")
|
2
|
+
|
3
|
+
module Hlockey
|
4
|
+
class Player
|
5
|
+
attr_accessor(:team, :stats)
|
6
|
+
attr_reader(:to_s)
|
7
|
+
|
8
|
+
@cached_chain = nil
|
9
|
+
|
10
|
+
# @param team [Team]
|
11
|
+
# @param prng [#rand]
|
12
|
+
def initialize(team, prng = Random)
|
13
|
+
@to_s = make_name(prng)
|
14
|
+
@team = team
|
15
|
+
# Generate stats
|
16
|
+
@stats = {}
|
17
|
+
%i[offense defense agility].each { |stat| @stats[stat] = prng.rand * 5 }
|
18
|
+
end
|
19
|
+
|
20
|
+
# @return [Hash<Symbol => String>]
|
21
|
+
def stat_strings
|
22
|
+
@stats.transform_values do |stat|
|
23
|
+
stat.instance_of?(String) ? stat : stat.round(2).to_s.ljust(4, "0")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def make_name(prng)
|
30
|
+
Array.new(2) do
|
31
|
+
combination = "__"
|
32
|
+
next_letter = ""
|
33
|
+
result = ""
|
34
|
+
|
35
|
+
10.times do
|
36
|
+
next_letters = name_chain[combination]
|
37
|
+
break if next_letters.nil?
|
38
|
+
|
39
|
+
cumulative_weights = []
|
40
|
+
next_letters.each_value do |v|
|
41
|
+
cumulative_weights << v + (cumulative_weights.last or 0)
|
42
|
+
end
|
43
|
+
|
44
|
+
next_letter = Utils.weighted_random(next_letters, prng)
|
45
|
+
break if next_letter == "_"
|
46
|
+
|
47
|
+
result += next_letter
|
48
|
+
combination = combination[1] + next_letter
|
49
|
+
end
|
50
|
+
|
51
|
+
result
|
52
|
+
end.join(" ")
|
53
|
+
end
|
54
|
+
|
55
|
+
def name_chain
|
56
|
+
if @cached_chain.nil?
|
57
|
+
@cached_chain = {}
|
58
|
+
File.open(File.expand_path("../../data/external/names.txt", __dir__)).each do |n|
|
59
|
+
name = "__#{n.chomp}__"
|
60
|
+
(name.length - 3).times do |i|
|
61
|
+
combination = name[i, 2]
|
62
|
+
next_letter = name[i + 2]
|
63
|
+
@cached_chain[combination] ||= {}
|
64
|
+
@cached_chain[combination][next_letter] ||= 0
|
65
|
+
@cached_chain[combination][next_letter] += 1
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
@cached_chain
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|