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
@@ -0,0 +1,191 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Basketball
|
4
|
+
module App
|
5
|
+
# Knows how to flatten a Coordinator instance and rehydrate one from JSON and/or a Ruby hash.
|
6
|
+
class CoordinatorRepository
|
7
|
+
GAME_CLASSES = {
|
8
|
+
'Exhibition' => Season::Exhibition,
|
9
|
+
'Regular' => Season::Regular
|
10
|
+
}.freeze
|
11
|
+
|
12
|
+
private_constant :GAME_CLASSES
|
13
|
+
|
14
|
+
attr_reader :store
|
15
|
+
|
16
|
+
def initialize(store: FileStore.new)
|
17
|
+
super()
|
18
|
+
|
19
|
+
@store = store
|
20
|
+
|
21
|
+
freeze
|
22
|
+
end
|
23
|
+
|
24
|
+
def load(path)
|
25
|
+
contents = store.read(path)
|
26
|
+
|
27
|
+
deserialize(contents).tap do |coordinator|
|
28
|
+
coordinator.send('id=', path)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def save(path, coordinator)
|
33
|
+
contents = serialize(coordinator)
|
34
|
+
|
35
|
+
store.write(path, contents)
|
36
|
+
|
37
|
+
coordinator.send('id=', path)
|
38
|
+
|
39
|
+
coordinator
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def deserialize(string)
|
45
|
+
hash = JSON.parse(string, symbolize_names: true)
|
46
|
+
|
47
|
+
from_h(hash)
|
48
|
+
end
|
49
|
+
|
50
|
+
def serialize(object)
|
51
|
+
to_h(object).to_json
|
52
|
+
end
|
53
|
+
|
54
|
+
def from_h(hash)
|
55
|
+
Season::Coordinator.new(
|
56
|
+
calendar: deserialize_calendar(hash[:calendar]),
|
57
|
+
current_date: Date.parse(hash[:current_date]),
|
58
|
+
results: deserialize_results(hash[:results]),
|
59
|
+
league: deserialize_league(hash[:league])
|
60
|
+
)
|
61
|
+
end
|
62
|
+
|
63
|
+
def to_h(coordinator)
|
64
|
+
{
|
65
|
+
calendar: serialize_calendar(coordinator.calendar),
|
66
|
+
current_date: coordinator.current_date.to_s,
|
67
|
+
results: serialize_results(coordinator.results),
|
68
|
+
league: serialize_league(coordinator.league)
|
69
|
+
}
|
70
|
+
end
|
71
|
+
|
72
|
+
# Serialization
|
73
|
+
|
74
|
+
def serialize_player(player)
|
75
|
+
{
|
76
|
+
id: player.id,
|
77
|
+
overall: player.overall,
|
78
|
+
position: player.position&.code
|
79
|
+
}
|
80
|
+
end
|
81
|
+
|
82
|
+
def serialize_games(games)
|
83
|
+
games.map { |game| serialize_game(game) }
|
84
|
+
end
|
85
|
+
|
86
|
+
def serialize_calendar(calendar)
|
87
|
+
{
|
88
|
+
preseason_start_date: calendar.preseason_start_date.to_s,
|
89
|
+
preseason_end_date: calendar.preseason_end_date.to_s,
|
90
|
+
season_start_date: calendar.season_start_date.to_s,
|
91
|
+
season_end_date: calendar.season_end_date.to_s,
|
92
|
+
games: serialize_games(calendar.games)
|
93
|
+
}
|
94
|
+
end
|
95
|
+
|
96
|
+
def serialize_game(game)
|
97
|
+
{
|
98
|
+
type: game.class.name.split('::').last,
|
99
|
+
date: game.date.to_s,
|
100
|
+
home_opponent: game.home_opponent.id,
|
101
|
+
away_opponent: game.away_opponent.id
|
102
|
+
}
|
103
|
+
end
|
104
|
+
|
105
|
+
def serialize_result(result)
|
106
|
+
{
|
107
|
+
game: serialize_game(result.game),
|
108
|
+
home_score: result.home_score,
|
109
|
+
away_score: result.away_score
|
110
|
+
}
|
111
|
+
end
|
112
|
+
|
113
|
+
def serialize_league(league)
|
114
|
+
{
|
115
|
+
teams: league.teams.map { |team| serialize_team(team) }
|
116
|
+
}
|
117
|
+
end
|
118
|
+
|
119
|
+
def serialize_team(team)
|
120
|
+
{
|
121
|
+
id: team.id,
|
122
|
+
players: team.players.map { |player| serialize_player(player) }
|
123
|
+
}
|
124
|
+
end
|
125
|
+
|
126
|
+
def serialize_results(results)
|
127
|
+
results.map do |result|
|
128
|
+
serialize_result(result)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# Deserialization
|
133
|
+
|
134
|
+
def deserialize_player(player_hash)
|
135
|
+
Org::Player.new(
|
136
|
+
id: player_hash[:id],
|
137
|
+
overall: player_hash[:overall],
|
138
|
+
position: Org::Position.new(player_hash[:position])
|
139
|
+
)
|
140
|
+
end
|
141
|
+
|
142
|
+
def deserialize_league(league_hash)
|
143
|
+
team_hashes = league_hash[:teams] || []
|
144
|
+
|
145
|
+
teams = team_hashes.map do |team_hash|
|
146
|
+
players = (team_hash[:players] || []).map { |player_hash| deserialize_player(player_hash) }
|
147
|
+
|
148
|
+
Org::Team.new(id: team_hash[:id], players:)
|
149
|
+
end
|
150
|
+
|
151
|
+
Org::League.new(teams:)
|
152
|
+
end
|
153
|
+
|
154
|
+
def deserialize_calendar(calendar_hash)
|
155
|
+
Season::Calendar.new(
|
156
|
+
preseason_start_date: Date.parse(calendar_hash[:preseason_start_date]),
|
157
|
+
preseason_end_date: Date.parse(calendar_hash[:preseason_end_date]),
|
158
|
+
season_start_date: Date.parse(calendar_hash[:season_start_date]),
|
159
|
+
season_end_date: Date.parse(calendar_hash[:season_end_date]),
|
160
|
+
games: deserialize_games(calendar_hash[:games])
|
161
|
+
)
|
162
|
+
end
|
163
|
+
|
164
|
+
def deserialize_games(game_hashes)
|
165
|
+
(game_hashes || []).map { |game_hash| deserialize_game(game_hash) }
|
166
|
+
end
|
167
|
+
|
168
|
+
def deserialize_game(game_hash)
|
169
|
+
GAME_CLASSES.fetch(game_hash[:type]).new(
|
170
|
+
date: Date.parse(game_hash[:date]),
|
171
|
+
home_opponent: Season::Opponent.new(id: game_hash[:home_opponent]),
|
172
|
+
away_opponent: Season::Opponent.new(id: game_hash[:away_opponent])
|
173
|
+
)
|
174
|
+
end
|
175
|
+
|
176
|
+
def deserialize_results(result_hashes)
|
177
|
+
(result_hashes || []).map do |result_hash|
|
178
|
+
deserialize_result(result_hash)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def deserialize_result(result_hash)
|
183
|
+
Season::Result.new(
|
184
|
+
game: deserialize_game(result_hash[:game]),
|
185
|
+
home_score: result_hash[:home_score],
|
186
|
+
away_score: result_hash[:away_score]
|
187
|
+
)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Basketball
|
4
|
+
module App
|
5
|
+
# Knows how to read and write documents to disk.
|
6
|
+
class FileStore
|
7
|
+
def read(path)
|
8
|
+
File.read(path)
|
9
|
+
end
|
10
|
+
|
11
|
+
def write(path, contents)
|
12
|
+
dir = File.dirname(path)
|
13
|
+
|
14
|
+
FileUtils.mkdir_p(dir)
|
15
|
+
|
16
|
+
File.write(path, contents)
|
17
|
+
|
18
|
+
nil
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,212 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Basketball
|
4
|
+
module App
|
5
|
+
# Examples:
|
6
|
+
# exe/basketball-room -o tmp/draft.json
|
7
|
+
# exe/basketball-room -i tmp/draft.json -o tmp/draft-wip.json -s 26 -p P-5,P-10 -t 10
|
8
|
+
# exe/basketball-room -i tmp/draft-wip.json -x 2
|
9
|
+
# exe/basketball-room -i tmp/draft-wip.json -g -t 10
|
10
|
+
# exe/basketball-room -i tmp/draft-wip.json -s 30 -t 10
|
11
|
+
# exe/basketball-room -i tmp/draft-wip.json -ale
|
12
|
+
#
|
13
|
+
# exe/basketball-room -o tmp/draft-wip.json -ale
|
14
|
+
class RoomCLI
|
15
|
+
class PlayerNotFound < StandardError; end
|
16
|
+
|
17
|
+
attr_reader :opts, :io, :repository
|
18
|
+
|
19
|
+
def initialize(args:, io: $stdout)
|
20
|
+
@io = io
|
21
|
+
@opts = slop_parse(args)
|
22
|
+
@repository = RoomRepository.new
|
23
|
+
|
24
|
+
if opts[:input].to_s.empty? && opts[:output].to_s.empty?
|
25
|
+
io.puts('Input and/or output paths are required.')
|
26
|
+
|
27
|
+
exit
|
28
|
+
end
|
29
|
+
|
30
|
+
freeze
|
31
|
+
end
|
32
|
+
|
33
|
+
def invoke!
|
34
|
+
room = load_room
|
35
|
+
|
36
|
+
execute(room)
|
37
|
+
status(room)
|
38
|
+
write(room)
|
39
|
+
events(room)
|
40
|
+
league(room)
|
41
|
+
query(room)
|
42
|
+
|
43
|
+
self
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def status(room)
|
49
|
+
io.puts
|
50
|
+
io.puts('Status')
|
51
|
+
|
52
|
+
if room.done?
|
53
|
+
io.puts('Draft is complete!')
|
54
|
+
else
|
55
|
+
round = room.round
|
56
|
+
round_pick = room.round_pick
|
57
|
+
front_office = room.front_office
|
58
|
+
|
59
|
+
io.puts("#{room.remaining_picks} Remaining pick(s)")
|
60
|
+
io.puts("Up Next: Round #{round} pick #{round_pick} for #{front_office}")
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def slop_parse(args)
|
65
|
+
Slop.parse(args) do |o|
|
66
|
+
o.banner = 'Usage: basketball-room [options] ...'
|
67
|
+
|
68
|
+
o.string '-i', '--input', 'Path to load the Room from. If omitted then a new draft will be generated.'
|
69
|
+
o.string '-o', '--output', 'Path to write the room to (if omitted then input path will be used)'
|
70
|
+
o.integer '-s', '--simulate', 'Number of picks to simulate (default is 0).', default: 0
|
71
|
+
o.bool '-a', '--simulate-all', 'Simulate the rest of the draft', default: false
|
72
|
+
o.array '-p', '--picks', 'Comma-separated list of ordered player IDs to pick.', delimiter: ','
|
73
|
+
o.integer '-t', '--top', 'Output the top rated available players (default is 0).', default: 0
|
74
|
+
o.bool '-l', '--league', 'Output all teams and their picks', default: false
|
75
|
+
o.integer '-x', '--skip', 'Number of picks to skip (default is 0).', default: 0
|
76
|
+
o.bool '-e', '--events', 'Output event log.', default: false
|
77
|
+
|
78
|
+
o.on '-h', '--help', 'Print out help, like this is doing right now.' do
|
79
|
+
io.puts(o)
|
80
|
+
exit
|
81
|
+
end
|
82
|
+
end.to_h
|
83
|
+
end
|
84
|
+
|
85
|
+
def load_room
|
86
|
+
if opts[:input].to_s.empty?
|
87
|
+
io.puts('Input path was not provided, generating fresh front_offices and players')
|
88
|
+
|
89
|
+
generate_draft
|
90
|
+
else
|
91
|
+
io.puts("Draft loaded from: #{opts[:input]}")
|
92
|
+
|
93
|
+
read
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def generate_draft
|
98
|
+
front_offices = 30.times.map do |i|
|
99
|
+
Draft::FrontOffice.new(
|
100
|
+
id: "T-#{i + 1}"
|
101
|
+
)
|
102
|
+
end
|
103
|
+
|
104
|
+
players = 450.times.map do |i|
|
105
|
+
Org::Player.new(
|
106
|
+
id: "P-#{i + 1}",
|
107
|
+
overall: (20..100).to_a.sample,
|
108
|
+
position: Org::Position.random
|
109
|
+
)
|
110
|
+
end
|
111
|
+
|
112
|
+
Draft::Room.new(rounds: 12, players:, front_offices:)
|
113
|
+
end
|
114
|
+
|
115
|
+
def league(room)
|
116
|
+
return unless opts[:league]
|
117
|
+
|
118
|
+
io.puts
|
119
|
+
io.puts(room.league)
|
120
|
+
end
|
121
|
+
|
122
|
+
def events(room)
|
123
|
+
return unless opts[:events]
|
124
|
+
|
125
|
+
io.puts
|
126
|
+
io.puts('Event Log')
|
127
|
+
|
128
|
+
puts room.events
|
129
|
+
end
|
130
|
+
|
131
|
+
def query(room)
|
132
|
+
top = opts[:top]
|
133
|
+
|
134
|
+
return if top <= 0
|
135
|
+
|
136
|
+
players = room.undrafted_players.sort_by(&:overall).reverse.take(opts[:top])
|
137
|
+
|
138
|
+
io.puts
|
139
|
+
io.puts("Top #{top} available players")
|
140
|
+
io.puts(players)
|
141
|
+
end
|
142
|
+
|
143
|
+
def read
|
144
|
+
repository.load(opts[:input])
|
145
|
+
end
|
146
|
+
|
147
|
+
# rubocop:disable Metrics/AbcSize
|
148
|
+
def execute(room)
|
149
|
+
event_count = 0
|
150
|
+
|
151
|
+
io.puts
|
152
|
+
io.puts('New Events')
|
153
|
+
|
154
|
+
(opts[:picks] || []).each do |id|
|
155
|
+
break if room.done?
|
156
|
+
|
157
|
+
player = room.players.find { |p| p.id == id.to_s.upcase }
|
158
|
+
|
159
|
+
raise PlayerNotFound, "player not found by id: #{id}" unless player
|
160
|
+
|
161
|
+
event = room.pick!(player)
|
162
|
+
|
163
|
+
io.puts(event)
|
164
|
+
|
165
|
+
event_count += 1
|
166
|
+
end
|
167
|
+
|
168
|
+
opts[:skip].times do
|
169
|
+
event = room.skip!
|
170
|
+
|
171
|
+
io.puts(event)
|
172
|
+
|
173
|
+
event_count += 1
|
174
|
+
end
|
175
|
+
|
176
|
+
opts[:simulate].times do
|
177
|
+
room.sim!
|
178
|
+
|
179
|
+
event_count += 1
|
180
|
+
end
|
181
|
+
|
182
|
+
if opts[:simulate_all]
|
183
|
+
room.sim_rest! do |event|
|
184
|
+
io.puts(event)
|
185
|
+
|
186
|
+
event_count += 1
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
io.puts("Generated #{event_count} new event(s)")
|
191
|
+
|
192
|
+
nil
|
193
|
+
end
|
194
|
+
# rubocop:enable Metrics/AbcSize
|
195
|
+
|
196
|
+
def output_default_to_input
|
197
|
+
opts[:output].to_s.empty? ? opts[:input] : opts[:output]
|
198
|
+
end
|
199
|
+
|
200
|
+
def write(room)
|
201
|
+
output = output_default_to_input
|
202
|
+
|
203
|
+
repository.save(output, room)
|
204
|
+
|
205
|
+
io.puts
|
206
|
+
io.puts("Draft written to: #{output}")
|
207
|
+
|
208
|
+
nil
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
@@ -0,0 +1,189 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Basketball
|
4
|
+
module App
|
5
|
+
# Can load and save Room objects to JSON files.
|
6
|
+
class RoomRepository
|
7
|
+
PICK_EVENT = 'Pick'
|
8
|
+
SKIP_EVENT = 'Skip'
|
9
|
+
|
10
|
+
private_constant :PICK_EVENT, :SKIP_EVENT
|
11
|
+
|
12
|
+
attr_reader :store
|
13
|
+
|
14
|
+
def initialize(store: FileStore.new)
|
15
|
+
super()
|
16
|
+
|
17
|
+
@store = store
|
18
|
+
|
19
|
+
freeze
|
20
|
+
end
|
21
|
+
|
22
|
+
def load(path)
|
23
|
+
contents = store.read(path)
|
24
|
+
|
25
|
+
room = deserialize(contents)
|
26
|
+
|
27
|
+
room.send('id=', path)
|
28
|
+
|
29
|
+
room
|
30
|
+
end
|
31
|
+
|
32
|
+
def save(path, room)
|
33
|
+
contents = serialize(room)
|
34
|
+
|
35
|
+
store.write(path, contents)
|
36
|
+
|
37
|
+
room.send('id=', path)
|
38
|
+
|
39
|
+
room
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def deserialize(string)
|
45
|
+
hash = JSON.parse(string, symbolize_names: true)
|
46
|
+
|
47
|
+
from_h(hash)
|
48
|
+
end
|
49
|
+
|
50
|
+
def serialize(object)
|
51
|
+
to_h(object).to_json
|
52
|
+
end
|
53
|
+
|
54
|
+
def from_h(hash)
|
55
|
+
front_offices = deserialize_front_offices(hash[:front_offices])
|
56
|
+
players = deserialize_players(hash[:players])
|
57
|
+
events = deserialize_events(hash[:events], players:, front_offices:)
|
58
|
+
|
59
|
+
Draft::Room.new(
|
60
|
+
rounds: hash[:rounds],
|
61
|
+
front_offices:,
|
62
|
+
players:,
|
63
|
+
events:
|
64
|
+
)
|
65
|
+
end
|
66
|
+
|
67
|
+
def to_h(room)
|
68
|
+
{
|
69
|
+
rounds: room.rounds,
|
70
|
+
front_offices: room.front_offices.map { |fo| serialize_front_office(fo) },
|
71
|
+
players: room.players.map { |p| serialize_player(p) },
|
72
|
+
events: serialize_events(room.events)
|
73
|
+
}
|
74
|
+
end
|
75
|
+
|
76
|
+
# Serialization
|
77
|
+
|
78
|
+
def serialize_player(player)
|
79
|
+
{
|
80
|
+
id: player.id,
|
81
|
+
overall: player.overall,
|
82
|
+
position: player.position&.code
|
83
|
+
}
|
84
|
+
end
|
85
|
+
|
86
|
+
def serialize_front_office(front_office)
|
87
|
+
{
|
88
|
+
id: front_office.id,
|
89
|
+
risk_level: front_office.risk_level
|
90
|
+
}
|
91
|
+
end
|
92
|
+
|
93
|
+
def serialize_events(events)
|
94
|
+
events.map do |event|
|
95
|
+
case event
|
96
|
+
when Draft::Pick
|
97
|
+
serialize_pick(event)
|
98
|
+
when Draft::Skip
|
99
|
+
serialize_skip(event)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def serialize_pick(event)
|
105
|
+
{
|
106
|
+
type: PICK_EVENT,
|
107
|
+
front_office: event.front_office.id,
|
108
|
+
pick: event.pick,
|
109
|
+
round: event.round,
|
110
|
+
round_pick: event.round_pick,
|
111
|
+
auto: event.auto,
|
112
|
+
player: event.player.id
|
113
|
+
}
|
114
|
+
end
|
115
|
+
|
116
|
+
def serialize_skip(event)
|
117
|
+
{
|
118
|
+
type: SKIP_EVENT,
|
119
|
+
front_office: event.front_office.id,
|
120
|
+
pick: event.pick,
|
121
|
+
round: event.round,
|
122
|
+
round_pick: event.round_pick
|
123
|
+
}
|
124
|
+
end
|
125
|
+
|
126
|
+
# Deserialization
|
127
|
+
|
128
|
+
def deserialize_player(player_hash)
|
129
|
+
Org::Player.new(
|
130
|
+
id: player_hash[:id],
|
131
|
+
overall: player_hash[:overall],
|
132
|
+
position: Org::Position.new(player_hash[:position])
|
133
|
+
)
|
134
|
+
end
|
135
|
+
|
136
|
+
def deserialize_front_office(hash)
|
137
|
+
Draft::FrontOffice.new(
|
138
|
+
id: hash[:id],
|
139
|
+
risk_level: hash[:risk_level]
|
140
|
+
)
|
141
|
+
end
|
142
|
+
|
143
|
+
def deserialize_front_offices(hashes)
|
144
|
+
(hashes || []).map { |fo| deserialize_front_office(fo) }
|
145
|
+
end
|
146
|
+
|
147
|
+
def deserialize_players(hashes)
|
148
|
+
(hashes || []).map { |hash| deserialize_player(hash) }
|
149
|
+
end
|
150
|
+
|
151
|
+
def deserialize_pick(hash, players:, front_office:)
|
152
|
+
player_id = hash[:player]
|
153
|
+
player = players.find { |p| p.id == player_id }
|
154
|
+
|
155
|
+
Draft::Pick.new(
|
156
|
+
front_office:,
|
157
|
+
pick: hash[:pick],
|
158
|
+
round: hash[:round],
|
159
|
+
round_pick: hash[:round_pick],
|
160
|
+
player:,
|
161
|
+
auto: hash[:auto]
|
162
|
+
)
|
163
|
+
end
|
164
|
+
|
165
|
+
def deserialize_skip(hash, front_office:)
|
166
|
+
Draft::Skip.new(
|
167
|
+
front_office:,
|
168
|
+
pick: hash[:pick],
|
169
|
+
round: hash[:round],
|
170
|
+
round_pick: hash[:round_pick]
|
171
|
+
)
|
172
|
+
end
|
173
|
+
|
174
|
+
def deserialize_events(hashes, players:, front_offices:)
|
175
|
+
(hashes || []).map do |hash|
|
176
|
+
front_office_id = hash[:front_office]
|
177
|
+
front_office = front_offices.find { |fo| fo.id == front_office_id }
|
178
|
+
|
179
|
+
case hash[:type]
|
180
|
+
when PICK_EVENT
|
181
|
+
deserialize_pick(hash, players:, front_office:)
|
182
|
+
when SKIP_EVENT
|
183
|
+
deserialize_skip(hash, front_office:)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Services
|
4
|
+
require_relative 'app/file_store'
|
5
|
+
|
6
|
+
# Repositories
|
7
|
+
require_relative 'app/coordinator_repository'
|
8
|
+
require_relative 'app/room_repository'
|
9
|
+
|
10
|
+
# Controllers
|
11
|
+
require_relative 'app/coordinator_cli'
|
12
|
+
require_relative 'app/room_cli'
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Basketball
|
4
|
+
module Draft
|
5
|
+
# An assessment is given to a front office when it needs the front office to make a pick.
|
6
|
+
# This is essentially just a data-transfer object to get information to a front office to make a pick.
|
7
|
+
class Assessment
|
8
|
+
attr_reader :drafted_players,
|
9
|
+
:pick,
|
10
|
+
:round_pick,
|
11
|
+
:round,
|
12
|
+
:undrafted_players
|
13
|
+
|
14
|
+
def initialize(
|
15
|
+
drafted_players:,
|
16
|
+
pick:,
|
17
|
+
round_pick:,
|
18
|
+
round:,
|
19
|
+
undrafted_players:
|
20
|
+
)
|
21
|
+
@drafted_players = drafted_players
|
22
|
+
@pick = pick
|
23
|
+
@round = round
|
24
|
+
@round_pick = round_pick
|
25
|
+
@undrafted_players = undrafted_players
|
26
|
+
|
27
|
+
freeze
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -1,9 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Basketball
|
4
|
-
module
|
4
|
+
module Draft
|
5
|
+
# Describes what all Room events have to have to be considered an "event".
|
5
6
|
class Event < ValueObject
|
6
|
-
|
7
|
+
value_reader :pick, :round, :round_pick, :front_office
|
7
8
|
|
8
9
|
def initialize(front_office:, pick:, round:, round_pick:)
|
9
10
|
super()
|
@@ -17,7 +18,7 @@ module Basketball
|
|
17
18
|
end
|
18
19
|
|
19
20
|
def to_s
|
20
|
-
"##{pick}
|
21
|
+
"[##{pick} R:#{round} P:#{round_pick}] #{front_office}"
|
21
22
|
end
|
22
23
|
end
|
23
24
|
end
|