basketball 0.0.10 → 0.0.11
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/README.md +28 -21
- data/basketball.gemspec +14 -8
- data/exe/basketball +91 -0
- data/lib/basketball/app/coordinator_cli.rb +42 -65
- data/lib/basketball/app/coordinator_repository.rb +9 -8
- data/lib/basketball/app/league_serializable.rb +69 -24
- data/lib/basketball/app/room_cli.rb +19 -32
- data/lib/basketball/draft/pick.rb +0 -7
- data/lib/basketball/draft/room.rb +7 -9
- data/lib/basketball/org/conference.rb +47 -0
- data/lib/basketball/org/division.rb +43 -0
- data/lib/basketball/org/has_divisions.rb +25 -0
- data/lib/basketball/org/has_players.rb +20 -0
- data/lib/basketball/org/has_teams.rb +24 -0
- data/lib/basketball/org/league.rb +59 -27
- data/lib/basketball/org.rb +12 -1
- data/lib/basketball/season/arena.rb +5 -5
- data/lib/basketball/season/calendar.rb +52 -22
- data/lib/basketball/season/coordinator.rb +22 -14
- data/lib/basketball/season/detail.rb +47 -0
- data/lib/basketball/season/exhibition.rb +1 -1
- data/lib/basketball/season/opponent.rb +6 -0
- data/lib/basketball/season/record.rb +92 -0
- data/lib/basketball/season/scheduler.rb +223 -0
- data/lib/basketball/season/standings.rb +56 -0
- data/lib/basketball/season.rb +6 -0
- data/lib/basketball/version.rb +1 -1
- metadata +17 -6
- /data/exe/{basketball-room → basketball-draft-room} +0 -0
- /data/exe/{basketball-coordinator → basketball-season-coordinator} +0 -0
@@ -21,10 +21,10 @@ module Basketball
|
|
21
21
|
:league
|
22
22
|
|
23
23
|
def_delegators :calendar,
|
24
|
-
:
|
25
|
-
:
|
26
|
-
:
|
27
|
-
:
|
24
|
+
:exhibition_start_date,
|
25
|
+
:exhibition_end_date,
|
26
|
+
:regular_start_date,
|
27
|
+
:regular_end_date,
|
28
28
|
:games,
|
29
29
|
:exhibitions_for,
|
30
30
|
:regulars_for,
|
@@ -69,13 +69,13 @@ module Basketball
|
|
69
69
|
end
|
70
70
|
|
71
71
|
def assert_current_date
|
72
|
-
if current_date <
|
73
|
-
raise OutOfBoundsError, "current date #{current_date} should be on or after #{
|
72
|
+
if current_date < exhibition_start_date
|
73
|
+
raise OutOfBoundsError, "current date #{current_date} should be on or after #{exhibition_start_date}"
|
74
74
|
end
|
75
75
|
|
76
|
-
return unless current_date >
|
76
|
+
return unless current_date > regular_end_date
|
77
77
|
|
78
|
-
raise OutOfBoundsError, "current date #{current_date} should be on or after #{
|
78
|
+
raise OutOfBoundsError, "current date #{current_date} should be on or after #{exhibition_start_date}"
|
79
79
|
end
|
80
80
|
|
81
81
|
def sim!
|
@@ -103,11 +103,11 @@ module Basketball
|
|
103
103
|
end
|
104
104
|
|
105
105
|
def total_days
|
106
|
-
(
|
106
|
+
(regular_end_date - exhibition_start_date).to_i
|
107
107
|
end
|
108
108
|
|
109
109
|
def days_left
|
110
|
-
(
|
110
|
+
(regular_end_date - current_date).to_i
|
111
111
|
end
|
112
112
|
|
113
113
|
def total_exhibitions
|
@@ -131,7 +131,7 @@ module Basketball
|
|
131
131
|
end
|
132
132
|
|
133
133
|
def done?
|
134
|
-
current_date ==
|
134
|
+
current_date == regular_end_date && games.length == results.length
|
135
135
|
end
|
136
136
|
|
137
137
|
def not_done?
|
@@ -153,6 +153,14 @@ module Basketball
|
|
153
153
|
end
|
154
154
|
end
|
155
155
|
|
156
|
+
def regular_results
|
157
|
+
results.select { |result| result.game.is_a?(Regular) }
|
158
|
+
end
|
159
|
+
|
160
|
+
def exhibition_results
|
161
|
+
results.select { |result| result.game.is_a?(Exhibition) }
|
162
|
+
end
|
163
|
+
|
156
164
|
private
|
157
165
|
|
158
166
|
attr_writer :arena
|
@@ -170,7 +178,7 @@ module Basketball
|
|
170
178
|
end
|
171
179
|
|
172
180
|
def increment_current_date!
|
173
|
-
return self if current_date >=
|
181
|
+
return self if current_date >= regular_end_date
|
174
182
|
|
175
183
|
@current_date = current_date + 1
|
176
184
|
|
@@ -184,9 +192,9 @@ module Basketball
|
|
184
192
|
end
|
185
193
|
|
186
194
|
def assert_known_teams(game)
|
187
|
-
raise UnknownTeamError, "unknown opponent: #{game.home_opponent}"
|
195
|
+
raise UnknownTeamError, "unknown opponent: #{game.home_opponent}" unless league.team?(game.home_opponent)
|
188
196
|
|
189
|
-
return
|
197
|
+
return if league.team?(game.away_opponent)
|
190
198
|
|
191
199
|
raise UnknownTeamError, "unknown opponent: #{game.away_opponent}"
|
192
200
|
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Basketball
|
4
|
+
module Season
|
5
|
+
# Describes a result from the perspective of a team.
|
6
|
+
class Detail < ValueObject
|
7
|
+
value_reader :date,
|
8
|
+
:home,
|
9
|
+
:opponent_score,
|
10
|
+
:opponent,
|
11
|
+
:score
|
12
|
+
|
13
|
+
alias home? home
|
14
|
+
|
15
|
+
def initialize(date:, home:, opponent:, opponent_score:, score:)
|
16
|
+
super()
|
17
|
+
|
18
|
+
raise ArgumentError, 'date is required' unless date
|
19
|
+
raise ArgumentError, 'opponent is required' unless opponent
|
20
|
+
raise ArgumentError, 'score is required' unless score
|
21
|
+
raise ArgumentError, 'opponent_score is required' unless opponent_score
|
22
|
+
raise ArgumentError, 'home is required' if home.nil?
|
23
|
+
raise CannotTieError, 'scores cannot be equal' if score == opponent_score
|
24
|
+
|
25
|
+
@date = date
|
26
|
+
@opponent = opponent
|
27
|
+
@score = score
|
28
|
+
@opponent_score = opponent_score
|
29
|
+
@home = home
|
30
|
+
|
31
|
+
freeze
|
32
|
+
end
|
33
|
+
|
34
|
+
def win?
|
35
|
+
score > opponent_score
|
36
|
+
end
|
37
|
+
|
38
|
+
def loss?
|
39
|
+
score < opponent_score
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_s
|
43
|
+
"[#{date}] #{win? ? 'Win' : 'Loss'} #{home? ? 'vs' : 'at'} #{opponent} (#{score}-#{opponent_score})"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -5,6 +5,12 @@ module Basketball
|
|
5
5
|
# Represents a team without a roster. Equal to a team by identity.
|
6
6
|
# A team's roster will not be known until the last minute (when it is game time).
|
7
7
|
class Opponent < Entity
|
8
|
+
class << self
|
9
|
+
def from(team)
|
10
|
+
new(id: team.id)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
8
14
|
def initialize(id:)
|
9
15
|
super(id)
|
10
16
|
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Basketball
|
4
|
+
module Season
|
5
|
+
# Represents a team within a Standings object. Each Record is comprised of Detail instances
|
6
|
+
# which are the game results in the perspective of a single Team.
|
7
|
+
class Record < Entity
|
8
|
+
class DetailAlreadyAddedError < StandardError; end
|
9
|
+
class OpponentNotFoundError < StandardError; end
|
10
|
+
|
11
|
+
def initialize(id:)
|
12
|
+
super(id)
|
13
|
+
|
14
|
+
@details_by_date = {}
|
15
|
+
|
16
|
+
details.each { |detail| add!(detail) }
|
17
|
+
|
18
|
+
freeze
|
19
|
+
end
|
20
|
+
|
21
|
+
def accept!(result)
|
22
|
+
if result.home_opponent == self
|
23
|
+
detail = Detail.new(
|
24
|
+
date: result.date,
|
25
|
+
opponent: result.away_opponent,
|
26
|
+
score: result.home_score,
|
27
|
+
opponent_score: result.away_score,
|
28
|
+
home: true
|
29
|
+
)
|
30
|
+
|
31
|
+
add!(detail)
|
32
|
+
elsif result.away_opponent == self
|
33
|
+
detail = Detail.new(
|
34
|
+
date: result.date,
|
35
|
+
opponent: result.home_opponent,
|
36
|
+
score: result.away_score,
|
37
|
+
opponent_score: result.home_score,
|
38
|
+
home: false
|
39
|
+
)
|
40
|
+
|
41
|
+
add!(detail)
|
42
|
+
else
|
43
|
+
raise OpponentNotFoundError, "#{result} has no opponent for #{self}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def detail_for(date)
|
48
|
+
details_by_date[date]
|
49
|
+
end
|
50
|
+
|
51
|
+
def details
|
52
|
+
details_by_date.values
|
53
|
+
end
|
54
|
+
|
55
|
+
def win_percentage
|
56
|
+
(win_count.to_f / game_count).round(3)
|
57
|
+
end
|
58
|
+
|
59
|
+
def game_count
|
60
|
+
details.length
|
61
|
+
end
|
62
|
+
|
63
|
+
def win_count
|
64
|
+
details.count(&:win?)
|
65
|
+
end
|
66
|
+
|
67
|
+
def loss_count
|
68
|
+
details.count(&:loss?)
|
69
|
+
end
|
70
|
+
|
71
|
+
def to_s
|
72
|
+
"[#{super}] #{win_count}-#{loss_count} (#{win_percentage})"
|
73
|
+
end
|
74
|
+
|
75
|
+
def <=>(other)
|
76
|
+
[win_count, win_percentage] <=> [other.win_count, other.win_percentage]
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
attr_reader :details_by_date
|
82
|
+
|
83
|
+
def add!(detail)
|
84
|
+
raise DetailAlreadyAddedError, "#{detail} already added for date" if detail_for(detail.date)
|
85
|
+
|
86
|
+
details_by_date[detail.date] = detail
|
87
|
+
|
88
|
+
self
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,223 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Basketball
|
4
|
+
module Season
|
5
|
+
# This is the service class responsible for actually picking out free dates and pairing up teams to
|
6
|
+
# play each other. This is a reasonable naive first pass at some underlying match-making algorithms
|
7
|
+
# but could definitely use some help with the complexity/runtime/etc.
|
8
|
+
class Scheduler
|
9
|
+
class BadConferencesSizeError < StandardError; end
|
10
|
+
class BadDivisionsSizeError < StandardError; end
|
11
|
+
class BadTeamsSizeError < StandardError; end
|
12
|
+
|
13
|
+
MIN_PRESEASON_GAMES_PER_TEAM = 3
|
14
|
+
MAX_PRESEASON_GAMES_PER_TEAM = 6
|
15
|
+
CONFERENCES_SIZE = 2
|
16
|
+
DIVISIONS_SIZE = 3
|
17
|
+
TEAMS_SIZE = 5
|
18
|
+
|
19
|
+
private_constant :MIN_PRESEASON_GAMES_PER_TEAM,
|
20
|
+
:MAX_PRESEASON_GAMES_PER_TEAM,
|
21
|
+
:CONFERENCES_SIZE,
|
22
|
+
:DIVISIONS_SIZE,
|
23
|
+
:TEAMS_SIZE
|
24
|
+
|
25
|
+
def schedule(league:, year: Time.now.year)
|
26
|
+
assert_properly_sized_league(league)
|
27
|
+
|
28
|
+
Calendar.new(**make_dates(year)).tap do |calendar|
|
29
|
+
schedule_exhibition!(calendar:, league:)
|
30
|
+
schedule_season!(calendar:, league:)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def make_dates(year)
|
37
|
+
{
|
38
|
+
exhibition_start_date: Date.new(year, 9, 30),
|
39
|
+
exhibition_end_date: Date.new(year, 10, 14),
|
40
|
+
regular_start_date: Date.new(year, 10, 18),
|
41
|
+
regular_end_date: Date.new(year + 1, 4, 29)
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
def assert_properly_sized_league(league)
|
46
|
+
if league.conferences.length != CONFERENCES_SIZE
|
47
|
+
raise BadConferencesSizeError, "there has to be #{CONFERENCES_SIZE} conferences"
|
48
|
+
end
|
49
|
+
|
50
|
+
league.conferences.each do |conference|
|
51
|
+
if conference.divisions.length != DIVISIONS_SIZE
|
52
|
+
raise BadDivisionsSizeError, "#{conference.id} should have exactly #{DIVISIONS_SIZE} divisions"
|
53
|
+
end
|
54
|
+
|
55
|
+
conference.divisions.each do |division|
|
56
|
+
if division.teams.length != TEAMS_SIZE
|
57
|
+
raise BadTeamsSizeError, "#{division.id} should have exactly #{TEAMS_SIZE} teams"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def base_matchup_count(league, team1, team2)
|
64
|
+
# Same Conference, Same Division
|
65
|
+
if league.division_for(team1) == league.division_for(team2)
|
66
|
+
4
|
67
|
+
# Same Conference, Different Division and one of 4/10 that play 3 times
|
68
|
+
elsif league.conference_for(team1) == league.conference_for(team2)
|
69
|
+
3
|
70
|
+
# Different Conference
|
71
|
+
else
|
72
|
+
2
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
77
|
+
# This method derives the plan for which a schedule can be generated from.
|
78
|
+
def matchup_plan(league)
|
79
|
+
matchups = {}
|
80
|
+
game_counts = league.teams.to_h { |t| [t, 0] }
|
81
|
+
teams = game_counts.keys
|
82
|
+
|
83
|
+
(0...teams.length).each do |i|
|
84
|
+
team1 = teams[i]
|
85
|
+
|
86
|
+
(i + 1...teams.length).each do |j|
|
87
|
+
team2 = teams[j]
|
88
|
+
key = [team1, team2].sort
|
89
|
+
count = base_matchup_count(league, team1, team2)
|
90
|
+
matchups[key] = count
|
91
|
+
game_counts[team1] += count
|
92
|
+
game_counts[team2] += count
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Each team will play 6 games against conference opponents in other divisions.
|
97
|
+
# The fours hash will be that plan.
|
98
|
+
find_fours(league).each do |team, opponents|
|
99
|
+
next if game_counts[team] == 82
|
100
|
+
|
101
|
+
opponents.each do |opponent|
|
102
|
+
next if game_counts[team] == 82
|
103
|
+
next if game_counts[opponent] == 82
|
104
|
+
|
105
|
+
game_counts[team] += 1
|
106
|
+
game_counts[opponent] += 1
|
107
|
+
|
108
|
+
key = [team, opponent].sort
|
109
|
+
|
110
|
+
matchups[key] += 1
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
matchups
|
115
|
+
end
|
116
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
117
|
+
|
118
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
119
|
+
# I am not liking this algorithm implementation at all but it will seemingly produce a valid
|
120
|
+
# result about 1 out of every 1000 cycles. I have yet to spot the assignment pattern to make
|
121
|
+
# this way more deterministic.
|
122
|
+
def find_fours(league)
|
123
|
+
balanced = false
|
124
|
+
count = 0
|
125
|
+
four_tracker = {}
|
126
|
+
|
127
|
+
until balanced
|
128
|
+
# Let's not completely thrash our CPUs in case this algorithm hits an infinite loop.
|
129
|
+
# Instead, lets hard-fail against a hard boundary.
|
130
|
+
raise ArgumentError, 'we spent too much CPU time and didnt resolve fours' if count > 100_000
|
131
|
+
|
132
|
+
four_tracker = league.teams.to_h { |team| [team, []] }
|
133
|
+
|
134
|
+
league.teams.each do |team|
|
135
|
+
opponents = league.cross_division_opponents_for(team).shuffle
|
136
|
+
|
137
|
+
opponents.each do |opponent|
|
138
|
+
if four_tracker[team].length < 6 && four_tracker[opponent].length < 6
|
139
|
+
four_tracker[opponent] << team
|
140
|
+
four_tracker[team] << opponent
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
good = true
|
146
|
+
|
147
|
+
# trip-wire: if one team isnt balanced then we are not balanced
|
148
|
+
four_tracker.each { |_k, v| good = false if v.length < 6 }
|
149
|
+
|
150
|
+
balanced = good
|
151
|
+
|
152
|
+
count += 1
|
153
|
+
end
|
154
|
+
|
155
|
+
four_tracker
|
156
|
+
end
|
157
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
158
|
+
|
159
|
+
def schedule_season!(calendar:, league:)
|
160
|
+
matchups = matchup_plan(league)
|
161
|
+
|
162
|
+
matchups.each do |(team1, team2), count|
|
163
|
+
candidates = calendar.available_regular_matchup_dates(team1, team2)
|
164
|
+
dates = candidates.sample(count)
|
165
|
+
games = balanced_games(dates, team1, team2)
|
166
|
+
|
167
|
+
games.each { |game| calendar.add!(game) }
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def balanced_games(dates, team1, team2)
|
172
|
+
dates.map.with_index(1) do |date, index|
|
173
|
+
home_opponent, away_opponent =
|
174
|
+
if index.even?
|
175
|
+
[Opponent.from(team1), Opponent.from(team2)]
|
176
|
+
else
|
177
|
+
[Opponent.from(team2), Opponent.from(team1)]
|
178
|
+
end
|
179
|
+
|
180
|
+
Regular.new(date:, home_opponent:, away_opponent:)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def schedule_exhibition!(calendar:, league:)
|
185
|
+
league.teams.each do |team|
|
186
|
+
current_games = calendar.exhibitions_for(opponent: team)
|
187
|
+
count = current_games.length
|
188
|
+
|
189
|
+
next if count >= MIN_PRESEASON_GAMES_PER_TEAM
|
190
|
+
|
191
|
+
other_teams = (league.teams - [team]).shuffle
|
192
|
+
|
193
|
+
other_teams.each do |other_team|
|
194
|
+
break if count > MIN_PRESEASON_GAMES_PER_TEAM
|
195
|
+
next if calendar.exhibitions_for(opponent: other_team).length >= MAX_PRESEASON_GAMES_PER_TEAM
|
196
|
+
|
197
|
+
candidates = calendar.available_exhibition_matchup_dates(team, other_team)
|
198
|
+
|
199
|
+
next if candidates.empty?
|
200
|
+
|
201
|
+
date = candidates.sample
|
202
|
+
game = random_exhibition_game(date, team, other_team)
|
203
|
+
|
204
|
+
calendar.add!(game)
|
205
|
+
|
206
|
+
count += 1
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def random_exhibition_game(date, team1, team2)
|
212
|
+
home_opponent, away_opponent =
|
213
|
+
if rand(1..2) == 1
|
214
|
+
[Opponent.from(team1), Opponent.from(team2)]
|
215
|
+
else
|
216
|
+
[Opponent.from(team2), Opponent.from(team1)]
|
217
|
+
end
|
218
|
+
|
219
|
+
Exhibition.new(date:, home_opponent:, away_opponent:)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Basketball
|
4
|
+
module Season
|
5
|
+
# Represents a League with each team's win/loss details.
|
6
|
+
class Standings
|
7
|
+
class TeamAlreadyRegisteredError < StandardError; end
|
8
|
+
class TeamNotRegisteredError < StandardError; end
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@records_by_id = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def register!(team)
|
15
|
+
raise TeamAlreadyRegisteredError, "#{team} already registered!" if team?(team)
|
16
|
+
|
17
|
+
records_by_id[team.id] = Record.new(id: team.id)
|
18
|
+
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def records
|
23
|
+
records_by_id.values
|
24
|
+
end
|
25
|
+
|
26
|
+
def record_for(team)
|
27
|
+
raise TeamNotRegisteredError, "#{team} not registered" unless team?(team)
|
28
|
+
|
29
|
+
records_by_id.fetch(team.id)
|
30
|
+
end
|
31
|
+
|
32
|
+
def accept!(result)
|
33
|
+
[
|
34
|
+
record_for(result.home_opponent),
|
35
|
+
record_for(result.away_opponent)
|
36
|
+
].each do |record|
|
37
|
+
record.accept!(result)
|
38
|
+
end
|
39
|
+
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
43
|
+
def team?(team)
|
44
|
+
records_by_id.key?(team.id)
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_s
|
48
|
+
records.sort.reverse.map.with_index(1) { |r, i| "##{i} #{r}" }.join("\n")
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
attr_reader :records_by_id
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/basketball/season.rb
CHANGED
@@ -12,5 +12,11 @@ require_relative 'season/opponent'
|
|
12
12
|
require_relative 'season/exhibition'
|
13
13
|
require_relative 'season/regular'
|
14
14
|
|
15
|
+
# Standings
|
16
|
+
require_relative 'season/detail'
|
17
|
+
require_relative 'season/record'
|
18
|
+
require_relative 'season/standings'
|
19
|
+
|
15
20
|
# Specific
|
16
21
|
require_relative 'season/coordinator'
|
22
|
+
require_relative 'season/scheduler'
|
data/lib/basketball/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: basketball
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matthew Ruggio
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-06-
|
11
|
+
date: 2023-06-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: slop
|
@@ -31,8 +31,9 @@ description: " This library is meant to serve as the domain for a basketball
|
|
31
31
|
email:
|
32
32
|
- mattruggio@icloud.com
|
33
33
|
executables:
|
34
|
-
- basketball
|
35
|
-
- basketball-
|
34
|
+
- basketball
|
35
|
+
- basketball-season-coordinator
|
36
|
+
- basketball-draft-room
|
36
37
|
extensions: []
|
37
38
|
extra_rdoc_files: []
|
38
39
|
files:
|
@@ -48,8 +49,9 @@ files:
|
|
48
49
|
- README.md
|
49
50
|
- Rakefile
|
50
51
|
- basketball.gemspec
|
51
|
-
- exe/basketball
|
52
|
-
- exe/basketball-room
|
52
|
+
- exe/basketball
|
53
|
+
- exe/basketball-draft-room
|
54
|
+
- exe/basketball-season-coordinator
|
53
55
|
- lib/basketball.rb
|
54
56
|
- lib/basketball/app.rb
|
55
57
|
- lib/basketball/app/coordinator_cli.rb
|
@@ -71,6 +73,11 @@ files:
|
|
71
73
|
- lib/basketball/draft/skip.rb
|
72
74
|
- lib/basketball/entity.rb
|
73
75
|
- lib/basketball/org.rb
|
76
|
+
- lib/basketball/org/conference.rb
|
77
|
+
- lib/basketball/org/division.rb
|
78
|
+
- lib/basketball/org/has_divisions.rb
|
79
|
+
- lib/basketball/org/has_players.rb
|
80
|
+
- lib/basketball/org/has_teams.rb
|
74
81
|
- lib/basketball/org/league.rb
|
75
82
|
- lib/basketball/org/player.rb
|
76
83
|
- lib/basketball/org/position.rb
|
@@ -79,12 +86,16 @@ files:
|
|
79
86
|
- lib/basketball/season/arena.rb
|
80
87
|
- lib/basketball/season/calendar.rb
|
81
88
|
- lib/basketball/season/coordinator.rb
|
89
|
+
- lib/basketball/season/detail.rb
|
82
90
|
- lib/basketball/season/exhibition.rb
|
83
91
|
- lib/basketball/season/game.rb
|
84
92
|
- lib/basketball/season/matchup.rb
|
85
93
|
- lib/basketball/season/opponent.rb
|
94
|
+
- lib/basketball/season/record.rb
|
86
95
|
- lib/basketball/season/regular.rb
|
87
96
|
- lib/basketball/season/result.rb
|
97
|
+
- lib/basketball/season/scheduler.rb
|
98
|
+
- lib/basketball/season/standings.rb
|
88
99
|
- lib/basketball/value_object.rb
|
89
100
|
- lib/basketball/value_object_dsl.rb
|
90
101
|
- lib/basketball/version.rb
|
File without changes
|
File without changes
|