basketball 0.0.7 → 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +9 -19
- data/CHANGELOG.md +1 -31
- data/README.md +72 -91
- data/basketball.gemspec +3 -6
- data/exe/{basketball-schedule → basketball-coordinator} +1 -1
- data/exe/{basketball-draft → basketball-room} +1 -1
- data/lib/basketball/app/coordinator_cli.rb +243 -0
- data/lib/basketball/app/coordinator_repository.rb +191 -0
- data/lib/basketball/app/file_store.rb +22 -0
- data/lib/basketball/app/room_cli.rb +212 -0
- data/lib/basketball/app/room_repository.rb +189 -0
- data/lib/basketball/app.rb +12 -0
- data/lib/basketball/draft/assessment.rb +31 -0
- data/lib/basketball/{drafting → draft}/event.rb +4 -3
- data/lib/basketball/draft/front_office.rb +99 -0
- data/lib/basketball/draft/pick.rb +32 -0
- data/lib/basketball/draft/room.rb +221 -0
- data/lib/basketball/{drafting/player_search.rb → draft/scout.rb} +5 -10
- data/lib/basketball/draft/skip.rb +12 -0
- data/lib/basketball/draft.rb +16 -0
- data/lib/basketball/entity.rb +10 -4
- data/lib/basketball/org/league.rb +73 -0
- data/lib/basketball/org/player.rb +26 -0
- data/lib/basketball/{drafting → org}/position.rb +3 -2
- data/lib/basketball/org/team.rb +38 -0
- data/lib/basketball/org.rb +12 -0
- data/lib/basketball/season/arena.rb +112 -0
- data/lib/basketball/season/calendar.rb +90 -0
- data/lib/basketball/season/coordinator.rb +239 -0
- data/lib/basketball/{scheduling/preseason_game.rb → season/exhibition.rb} +3 -2
- data/lib/basketball/season/game.rb +37 -0
- data/lib/basketball/season/matchup.rb +27 -0
- data/lib/basketball/season/opponent.rb +15 -0
- data/lib/basketball/season/regular.rb +9 -0
- data/lib/basketball/season/result.rb +37 -0
- data/lib/basketball/season.rb +16 -0
- data/lib/basketball/value_object.rb +4 -1
- data/lib/basketball/version.rb +1 -1
- data/lib/basketball.rb +11 -6
- metadata +40 -52
- data/lib/basketball/drafting/cli.rb +0 -235
- data/lib/basketball/drafting/engine.rb +0 -221
- data/lib/basketball/drafting/engine_serializer.rb +0 -186
- data/lib/basketball/drafting/front_office.rb +0 -92
- data/lib/basketball/drafting/league.rb +0 -70
- data/lib/basketball/drafting/pick_event.rb +0 -25
- data/lib/basketball/drafting/player.rb +0 -43
- data/lib/basketball/drafting/roster.rb +0 -37
- data/lib/basketball/drafting/sim_event.rb +0 -23
- data/lib/basketball/drafting/skip_event.rb +0 -13
- data/lib/basketball/drafting.rb +0 -9
- data/lib/basketball/scheduling/calendar.rb +0 -121
- data/lib/basketball/scheduling/calendar_serializer.rb +0 -94
- data/lib/basketball/scheduling/cli.rb +0 -198
- data/lib/basketball/scheduling/conference.rb +0 -57
- data/lib/basketball/scheduling/coordinator.rb +0 -180
- data/lib/basketball/scheduling/division.rb +0 -43
- data/lib/basketball/scheduling/game.rb +0 -32
- data/lib/basketball/scheduling/league.rb +0 -114
- data/lib/basketball/scheduling/league_serializer.rb +0 -99
- data/lib/basketball/scheduling/season_game.rb +0 -8
- data/lib/basketball/scheduling/team.rb +0 -21
- data/lib/basketball/scheduling.rb +0 -17
@@ -1,186 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'front_office'
|
4
|
-
require_relative 'player'
|
5
|
-
require_relative 'pick_event'
|
6
|
-
require_relative 'sim_event'
|
7
|
-
require_relative 'skip_event'
|
8
|
-
|
9
|
-
module Basketball
|
10
|
-
module Drafting
|
11
|
-
class EngineSerializer
|
12
|
-
EVENT_CLASSES = {
|
13
|
-
'PickEvent' => PickEvent,
|
14
|
-
'SimEvent' => SimEvent,
|
15
|
-
'SkipEvent' => SkipEvent
|
16
|
-
}.freeze
|
17
|
-
|
18
|
-
private_constant :EVENT_CLASSES
|
19
|
-
|
20
|
-
def to_hash(engine)
|
21
|
-
{
|
22
|
-
'info' => serialize_info(engine),
|
23
|
-
'engine' => serialize_engine(engine),
|
24
|
-
'league' => serialize_league(engine)
|
25
|
-
}
|
26
|
-
end
|
27
|
-
|
28
|
-
def from_hash(json)
|
29
|
-
front_offices = deserialize_front_offices(json)
|
30
|
-
players = deserialize_players(json)
|
31
|
-
events = deserialize_events(json, players, front_offices)
|
32
|
-
|
33
|
-
engine_opts = {
|
34
|
-
players:,
|
35
|
-
front_offices:,
|
36
|
-
events:
|
37
|
-
}
|
38
|
-
|
39
|
-
engine_opts[:rounds] = json.dig('engine', 'rounds') if json.dig('engine', 'rounds')
|
40
|
-
|
41
|
-
Engine.new(**engine_opts)
|
42
|
-
end
|
43
|
-
|
44
|
-
def deserialize(string)
|
45
|
-
json = JSON.parse(string)
|
46
|
-
|
47
|
-
from_hash(json)
|
48
|
-
end
|
49
|
-
|
50
|
-
def serialize(engine)
|
51
|
-
to_hash(engine).to_json
|
52
|
-
end
|
53
|
-
|
54
|
-
private
|
55
|
-
|
56
|
-
def serialize_engine(engine)
|
57
|
-
{
|
58
|
-
'rounds' => engine.rounds,
|
59
|
-
'front_offices' => serialize_front_offices(engine),
|
60
|
-
'players' => serialize_players(engine),
|
61
|
-
'events' => serialize_events(engine.events)
|
62
|
-
}
|
63
|
-
end
|
64
|
-
|
65
|
-
def serialize_info(engine)
|
66
|
-
{
|
67
|
-
'total_picks' => engine.total_picks,
|
68
|
-
'current_round' => engine.current_round,
|
69
|
-
'current_round_pick' => engine.current_round_pick,
|
70
|
-
'current_front_office' => engine.current_front_office&.id,
|
71
|
-
'current_pick' => engine.current_pick,
|
72
|
-
'remaining_picks' => engine.remaining_picks,
|
73
|
-
'done' => engine.done?
|
74
|
-
}
|
75
|
-
end
|
76
|
-
|
77
|
-
def serialize_league(engine)
|
78
|
-
league = engine.to_league
|
79
|
-
|
80
|
-
rosters = league.rosters.to_h do |roster|
|
81
|
-
[
|
82
|
-
roster.id,
|
83
|
-
{
|
84
|
-
'players' => roster.players.map(&:id)
|
85
|
-
}
|
86
|
-
]
|
87
|
-
end
|
88
|
-
|
89
|
-
{
|
90
|
-
'free_agents' => league.free_agents.map(&:id),
|
91
|
-
'rosters' => rosters
|
92
|
-
}
|
93
|
-
end
|
94
|
-
|
95
|
-
def serialize_front_offices(engine)
|
96
|
-
engine.front_offices.to_h do |front_office|
|
97
|
-
[
|
98
|
-
front_office.id,
|
99
|
-
{
|
100
|
-
'name' => front_office.name,
|
101
|
-
'fuzz' => front_office.fuzz,
|
102
|
-
'depth' => front_office.depth,
|
103
|
-
'prioritized_positions' => front_office.prioritized_positions.map(&:code)
|
104
|
-
}
|
105
|
-
]
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
def serialize_players(engine)
|
110
|
-
engine.players.to_h do |player|
|
111
|
-
[
|
112
|
-
player.id,
|
113
|
-
{
|
114
|
-
'first_name' => player.first_name,
|
115
|
-
'last_name' => player.last_name,
|
116
|
-
'overall' => player.overall,
|
117
|
-
'position' => player.position.code
|
118
|
-
}
|
119
|
-
]
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
def serialize_events(events)
|
124
|
-
events.map do |event|
|
125
|
-
{
|
126
|
-
'type' => event.class.name.split('::').last,
|
127
|
-
'front_office' => event.front_office.id,
|
128
|
-
'pick' => event.pick,
|
129
|
-
'round' => event.round,
|
130
|
-
'round_pick' => event.round_pick
|
131
|
-
}.tap do |hash|
|
132
|
-
hash['player'] = event.player.id if event.respond_to?(:player)
|
133
|
-
end
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
def deserialize_front_offices(json)
|
138
|
-
(json.dig('engine', 'front_offices') || []).map do |id, front_office_hash|
|
139
|
-
prioritized_positions = (front_office_hash['prioritized_positions'] || []).map do |v|
|
140
|
-
Position.new(v)
|
141
|
-
end
|
142
|
-
|
143
|
-
front_office_opts = {
|
144
|
-
id:,
|
145
|
-
name: front_office_hash['name'],
|
146
|
-
prioritized_positions:,
|
147
|
-
fuzz: front_office_hash['fuzz'],
|
148
|
-
depth: front_office_hash['depth']
|
149
|
-
}
|
150
|
-
|
151
|
-
FrontOffice.new(**front_office_opts)
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
def deserialize_players(json)
|
156
|
-
(json.dig('engine', 'players') || []).map do |id, player_hash|
|
157
|
-
player_opts = {
|
158
|
-
id:,
|
159
|
-
first_name: player_hash['first_name'],
|
160
|
-
last_name: player_hash['last_name'],
|
161
|
-
overall: player_hash['overall'],
|
162
|
-
position: Position.new(player_hash['position'])
|
163
|
-
}
|
164
|
-
|
165
|
-
Player.new(**player_opts)
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
def deserialize_events(json, players, front_offices)
|
170
|
-
(json.dig('engine', 'events') || []).map do |event_hash|
|
171
|
-
event_opts = event_hash.slice('pick', 'round', 'round_pick').merge(
|
172
|
-
front_office: front_offices.find { |t| t.id == event_hash['front_office'] }
|
173
|
-
)
|
174
|
-
|
175
|
-
class_constant = EVENT_CLASSES.fetch(event_hash['type'])
|
176
|
-
|
177
|
-
if [PickEvent, SimEvent].include?(class_constant)
|
178
|
-
event_opts[:player] = players.find { |p| p.id == event_hash['player'] }
|
179
|
-
end
|
180
|
-
|
181
|
-
class_constant.new(**event_opts)
|
182
|
-
end
|
183
|
-
end
|
184
|
-
end
|
185
|
-
end
|
186
|
-
end
|
@@ -1,92 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Basketball
|
4
|
-
module Drafting
|
5
|
-
class FrontOffice < Entity
|
6
|
-
MAX_DEPTH = 3
|
7
|
-
MAX_FUZZ = 2
|
8
|
-
MAX_POSITIONS = 12
|
9
|
-
|
10
|
-
private_constant :MAX_DEPTH, :MAX_FUZZ, :MAX_POSITIONS
|
11
|
-
|
12
|
-
attr_reader :prioritized_positions, :fuzz, :depth, :name
|
13
|
-
|
14
|
-
def initialize(id:, name: '', prioritized_positions: [], fuzz: rand(0..MAX_FUZZ), depth: rand(0..MAX_DEPTH))
|
15
|
-
super(id)
|
16
|
-
|
17
|
-
@name = name
|
18
|
-
@fuzz = fuzz.to_i
|
19
|
-
@depth = depth.to_i
|
20
|
-
@prioritized_positions = prioritized_positions
|
21
|
-
|
22
|
-
# fill in the rest of the queue here
|
23
|
-
need_count = MAX_POSITIONS - @prioritized_positions.length
|
24
|
-
|
25
|
-
@prioritized_positions += random_positions_queue[0...need_count]
|
26
|
-
|
27
|
-
freeze
|
28
|
-
end
|
29
|
-
|
30
|
-
def pick(undrafted_player_search:, drafted_players:, round:)
|
31
|
-
players = []
|
32
|
-
|
33
|
-
players = adaptive_search(undrafted_player_search:, drafted_players:) if depth >= round
|
34
|
-
players = balanced_search(undrafted_player_search:, drafted_players:) if players.empty?
|
35
|
-
players = top_players(undrafted_player_search:) if players.empty?
|
36
|
-
|
37
|
-
players[0..fuzz].sample
|
38
|
-
end
|
39
|
-
|
40
|
-
def to_s
|
41
|
-
"[#{super}] #{name}"
|
42
|
-
end
|
43
|
-
|
44
|
-
private
|
45
|
-
|
46
|
-
def adaptive_search(undrafted_player_search:, drafted_players:)
|
47
|
-
drafted_positions = drafted_players.map(&:position)
|
48
|
-
|
49
|
-
undrafted_player_search.query(exclude_positions: drafted_positions)
|
50
|
-
end
|
51
|
-
|
52
|
-
def balanced_search(undrafted_player_search:, drafted_players:)
|
53
|
-
players = []
|
54
|
-
|
55
|
-
# Try to find best pick for exact desired position.
|
56
|
-
# If you cant find one, then move to the next desired position until the end of the queue
|
57
|
-
available_prioritized_positions(drafted_players:).each do |position|
|
58
|
-
players = undrafted_player_search.query(position:)
|
59
|
-
|
60
|
-
break if players.any?
|
61
|
-
end
|
62
|
-
|
63
|
-
players = players.any? ? players : undrafted_player_search.query
|
64
|
-
end
|
65
|
-
|
66
|
-
def all_random_positions
|
67
|
-
Position::ALL_VALUES.to_a.shuffle.map { |v| Position.new(v) }
|
68
|
-
end
|
69
|
-
|
70
|
-
def random_positions_queue
|
71
|
-
all_random_positions + all_random_positions + [Position.random] + [Position.random]
|
72
|
-
end
|
73
|
-
|
74
|
-
def available_prioritized_positions(drafted_players:)
|
75
|
-
drafted_positions = drafted_players.map(&:position)
|
76
|
-
queue = prioritized_positions.dup
|
77
|
-
|
78
|
-
drafted_positions.each do |drafted_position|
|
79
|
-
index = queue.index(drafted_position)
|
80
|
-
|
81
|
-
next unless index
|
82
|
-
|
83
|
-
queue.delete_at(index)
|
84
|
-
|
85
|
-
queue << drafted_position
|
86
|
-
end
|
87
|
-
|
88
|
-
queue
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
@@ -1,70 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'roster'
|
4
|
-
|
5
|
-
module Basketball
|
6
|
-
module Drafting
|
7
|
-
class League
|
8
|
-
class RosterNotFoundError < StandardError; end
|
9
|
-
class RosterAlreadyAddedError < StandardError; end
|
10
|
-
|
11
|
-
attr_reader :free_agents, :rosters
|
12
|
-
|
13
|
-
def initialize(free_agents: [], front_offices: [])
|
14
|
-
@rosters = []
|
15
|
-
@free_agents = []
|
16
|
-
|
17
|
-
front_offices.each { |front_office| add_roster(front_office) }
|
18
|
-
free_agents.each { |p| register!(player: p) }
|
19
|
-
|
20
|
-
freeze
|
21
|
-
end
|
22
|
-
|
23
|
-
def roster(front_office)
|
24
|
-
rosters.find { |r| r == front_office }
|
25
|
-
end
|
26
|
-
|
27
|
-
def register!(player:, front_office: nil)
|
28
|
-
raise PlayerRequiredError, 'player is required' unless player
|
29
|
-
|
30
|
-
rosters.each do |roster|
|
31
|
-
if roster.registered?(player)
|
32
|
-
raise PlayerAlreadyRegisteredError,
|
33
|
-
"#{player} already registered to: #{roster.id}"
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
if free_agents.include?(player)
|
38
|
-
raise PlayerAlreadyRegisteredError,
|
39
|
-
"#{player} already registered as a free agent"
|
40
|
-
end
|
41
|
-
|
42
|
-
if front_office
|
43
|
-
roster = roster(front_office)
|
44
|
-
|
45
|
-
raise RosterNotFoundError, "Roster not found for: #{front_office}" unless roster
|
46
|
-
|
47
|
-
roster.sign!(player)
|
48
|
-
else
|
49
|
-
free_agents << player
|
50
|
-
end
|
51
|
-
|
52
|
-
self
|
53
|
-
end
|
54
|
-
|
55
|
-
def to_s
|
56
|
-
(['League'] + rosters.map(&:to_s)).join("\n")
|
57
|
-
end
|
58
|
-
|
59
|
-
private
|
60
|
-
|
61
|
-
def add_roster(front_office)
|
62
|
-
raise RosterAlreadyAddedError, "#{front_office} already added" if rosters.include?(front_office)
|
63
|
-
|
64
|
-
rosters << Roster.new(id: front_office.id, name: front_office.name)
|
65
|
-
|
66
|
-
self
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
@@ -1,25 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'event'
|
4
|
-
|
5
|
-
module Basketball
|
6
|
-
module Drafting
|
7
|
-
class PickEvent < Event
|
8
|
-
attr_reader_value :player
|
9
|
-
|
10
|
-
def initialize(front_office:, player:, pick:, round:, round_pick:)
|
11
|
-
super(front_office:, pick:, round:, round_pick:)
|
12
|
-
|
13
|
-
raise ArgumentError, 'player required' unless player
|
14
|
-
|
15
|
-
@player = player
|
16
|
-
|
17
|
-
freeze
|
18
|
-
end
|
19
|
-
|
20
|
-
def to_s
|
21
|
-
"#{player} picked #{super}"
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
@@ -1,43 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Basketball
|
4
|
-
module Drafting
|
5
|
-
class Player < Entity
|
6
|
-
STAR_THRESHOLD = 75
|
7
|
-
OVERALL_STAR_INDICATOR = '⭐'
|
8
|
-
|
9
|
-
private_constant :STAR_THRESHOLD, :OVERALL_STAR_INDICATOR
|
10
|
-
|
11
|
-
attr_reader :first_name, :last_name, :position, :overall
|
12
|
-
|
13
|
-
def initialize(id:, position:, first_name: '', last_name: '', overall: 0)
|
14
|
-
super(id)
|
15
|
-
|
16
|
-
raise ArgumentError, 'position is required' unless position
|
17
|
-
|
18
|
-
@first_name = first_name.to_s
|
19
|
-
@last_name = last_name.to_s
|
20
|
-
@position = position
|
21
|
-
@overall = overall.to_i
|
22
|
-
|
23
|
-
freeze
|
24
|
-
end
|
25
|
-
|
26
|
-
def full_name
|
27
|
-
"#{first_name.strip} #{last_name.strip}".strip
|
28
|
-
end
|
29
|
-
|
30
|
-
def to_s
|
31
|
-
"[#{super}] #{full_name} (#{position}) #{overall} #{star_indicators.join(', ')}".strip
|
32
|
-
end
|
33
|
-
|
34
|
-
private
|
35
|
-
|
36
|
-
def star_indicators
|
37
|
-
[].tap do |indicators|
|
38
|
-
indicators << OVERALL_STAR_INDICATOR if overall >= STAR_THRESHOLD
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
@@ -1,37 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Basketball
|
4
|
-
module Drafting
|
5
|
-
class Roster < Entity
|
6
|
-
class PlayerRequiredError < StandardError; end
|
7
|
-
|
8
|
-
attr_reader :name, :players
|
9
|
-
|
10
|
-
def initialize(id:, name: '', players: [])
|
11
|
-
super(id)
|
12
|
-
|
13
|
-
@name = name.to_s
|
14
|
-
@players = players.each { |p| register!(p) }
|
15
|
-
|
16
|
-
freeze
|
17
|
-
end
|
18
|
-
|
19
|
-
def registered?(player)
|
20
|
-
players.include?(player)
|
21
|
-
end
|
22
|
-
|
23
|
-
def sign!(player)
|
24
|
-
raise PlayerRequiredError, 'player is required' unless player
|
25
|
-
raise PlayerAlreadyRegisteredError, "#{player} already registered for #{id}" if registered?(player)
|
26
|
-
|
27
|
-
players << player
|
28
|
-
|
29
|
-
self
|
30
|
-
end
|
31
|
-
|
32
|
-
def to_s
|
33
|
-
(["[#{super}] #{name} Roster"] + players.map(&:to_s)).join("\n")
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Basketball
|
4
|
-
module Drafting
|
5
|
-
class SimEvent < Event
|
6
|
-
attr_reader_value :player
|
7
|
-
|
8
|
-
def initialize(front_office:, player:, pick:, round:, round_pick:)
|
9
|
-
super(front_office:, pick:, round:, round_pick:)
|
10
|
-
|
11
|
-
raise ArgumentError, 'player required' unless player
|
12
|
-
|
13
|
-
@player = player
|
14
|
-
|
15
|
-
freeze
|
16
|
-
end
|
17
|
-
|
18
|
-
def to_s
|
19
|
-
"#{player} auto-picked #{super}"
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
data/lib/basketball/drafting.rb
DELETED
@@ -1,121 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Basketball
|
4
|
-
module Scheduling
|
5
|
-
class Calendar < ValueObject
|
6
|
-
class TeamAlreadyBookedError < StandardError; end
|
7
|
-
class InvalidGameOrderError < StandardError; end
|
8
|
-
class OutOfBoundsError < StandardError; end
|
9
|
-
|
10
|
-
attr_reader :preseason_start_date,
|
11
|
-
:preseason_end_date,
|
12
|
-
:season_start_date,
|
13
|
-
:season_end_date
|
14
|
-
|
15
|
-
attr_reader_value :year, :games
|
16
|
-
|
17
|
-
def initialize(year:, games: [])
|
18
|
-
super()
|
19
|
-
|
20
|
-
raise ArgumentError, 'year is required' unless year
|
21
|
-
|
22
|
-
@year = year
|
23
|
-
@preseason_start_date = Date.new(year, 9, 30)
|
24
|
-
@preseason_end_date = Date.new(year, 10, 14)
|
25
|
-
@season_start_date = Date.new(year, 10, 18)
|
26
|
-
@season_end_date = Date.new(year + 1, 4, 29)
|
27
|
-
@games = []
|
28
|
-
|
29
|
-
games.each { |game| add!(game) }
|
30
|
-
|
31
|
-
freeze
|
32
|
-
end
|
33
|
-
|
34
|
-
def add!(game)
|
35
|
-
assert_in_bounds(game)
|
36
|
-
assert_free_date(game)
|
37
|
-
|
38
|
-
@games << game
|
39
|
-
|
40
|
-
self
|
41
|
-
end
|
42
|
-
|
43
|
-
def preseason_games_for(date: nil, team: nil)
|
44
|
-
games_for(date:, team:).select { |game| game.is_a?(PreseasonGame) }
|
45
|
-
end
|
46
|
-
|
47
|
-
def season_games_for(date: nil, team: nil)
|
48
|
-
games_for(date:, team:).select { |game| game.is_a?(SeasonGame) }
|
49
|
-
end
|
50
|
-
|
51
|
-
def games_for(date: nil, team: nil)
|
52
|
-
games.select do |game|
|
53
|
-
(date.nil? || game.date == date) &&
|
54
|
-
(team.nil? || (game.home_team == team || game.away_team == team))
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def available_preseason_dates_for(team)
|
59
|
-
all_preseason_dates - preseason_games_for(team:).map(&:date)
|
60
|
-
end
|
61
|
-
|
62
|
-
def available_season_dates_for(team)
|
63
|
-
all_season_dates - season_games_for(team:).map(&:date)
|
64
|
-
end
|
65
|
-
|
66
|
-
def available_preseason_matchup_dates(team1, team2)
|
67
|
-
available_team_dates = available_preseason_dates_for(team1)
|
68
|
-
available_other_team_dates = available_preseason_dates_for(team2)
|
69
|
-
|
70
|
-
available_team_dates & available_other_team_dates
|
71
|
-
end
|
72
|
-
|
73
|
-
def available_season_matchup_dates(team1, team2)
|
74
|
-
available_team_dates = available_season_dates_for(team1)
|
75
|
-
available_other_team_dates = available_season_dates_for(team2)
|
76
|
-
|
77
|
-
available_team_dates & available_other_team_dates
|
78
|
-
end
|
79
|
-
|
80
|
-
def teams
|
81
|
-
games.flat_map(&:teams)
|
82
|
-
end
|
83
|
-
|
84
|
-
def team(id)
|
85
|
-
teams.find { |t| t == Team.new(id:) }
|
86
|
-
end
|
87
|
-
|
88
|
-
private
|
89
|
-
|
90
|
-
def all_preseason_dates
|
91
|
-
(preseason_start_date..preseason_end_date).to_a
|
92
|
-
end
|
93
|
-
|
94
|
-
def all_season_dates
|
95
|
-
(season_start_date..season_end_date).to_a
|
96
|
-
end
|
97
|
-
|
98
|
-
def assert_free_date(game)
|
99
|
-
if games_for(date: game.date, team: game.home_team).any?
|
100
|
-
raise TeamAlreadyBookedError, "#{game.home_team} already playing on #{game.date}"
|
101
|
-
end
|
102
|
-
|
103
|
-
return unless games_for(date: game.date, team: game.away_team).any?
|
104
|
-
|
105
|
-
raise TeamAlreadyBookedError, "#{game.away_team} already playing on #{game.date}"
|
106
|
-
end
|
107
|
-
|
108
|
-
def assert_in_bounds(game)
|
109
|
-
if game.is_a?(PreseasonGame)
|
110
|
-
raise OutOfBoundsError, "#{game.date} is before preseason begins" if game.date < preseason_start_date
|
111
|
-
raise OutOfBoundsError, "#{game.date} is after preseason ends" if game.date > preseason_end_date
|
112
|
-
elsif game.is_a?(SeasonGame)
|
113
|
-
raise OutOfBoundsError, "#{game.date} is before season begins" if game.date < season_start_date
|
114
|
-
raise OutOfBoundsError, "#{game.date} is after season ends" if game.date > season_end_date
|
115
|
-
else
|
116
|
-
raise ArgumentError, "Dont know what this game type is: #{game.class.name}"
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|