basketball 0.0.8 → 0.0.9

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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +9 -19
  3. data/CHANGELOG.md +1 -39
  4. data/README.md +72 -93
  5. data/basketball.gemspec +3 -6
  6. data/exe/{basketball-season-scheduling → basketball-coordinator} +1 -1
  7. data/exe/{basketball-draft → basketball-room} +1 -1
  8. data/lib/basketball/app/coordinator_cli.rb +243 -0
  9. data/lib/basketball/app/coordinator_repository.rb +191 -0
  10. data/lib/basketball/app/file_store.rb +22 -0
  11. data/lib/basketball/{draft/cli.rb → app/room_cli.rb} +53 -76
  12. data/lib/basketball/app/room_repository.rb +189 -0
  13. data/lib/basketball/app.rb +12 -0
  14. data/lib/basketball/draft/assessment.rb +31 -0
  15. data/lib/basketball/draft/event.rb +3 -2
  16. data/lib/basketball/draft/front_office.rb +35 -28
  17. data/lib/basketball/draft/{pick_event.rb → pick.rb} +13 -6
  18. data/lib/basketball/draft/room.rb +119 -119
  19. data/lib/basketball/draft/{player_search.rb → scout.rb} +4 -9
  20. data/lib/basketball/draft/skip.rb +12 -0
  21. data/lib/basketball/draft.rb +13 -6
  22. data/lib/basketball/entity.rb +10 -4
  23. data/lib/basketball/org/league.rb +73 -0
  24. data/lib/basketball/org/player.rb +26 -0
  25. data/lib/basketball/{draft → org}/position.rb +3 -2
  26. data/lib/basketball/org/team.rb +38 -0
  27. data/lib/basketball/org.rb +12 -0
  28. data/lib/basketball/season/arena.rb +112 -0
  29. data/lib/basketball/season/calendar.rb +41 -72
  30. data/lib/basketball/season/coordinator.rb +185 -126
  31. data/lib/basketball/season/{preseason_game.rb → exhibition.rb} +2 -1
  32. data/lib/basketball/season/game.rb +15 -10
  33. data/lib/basketball/season/matchup.rb +27 -0
  34. data/lib/basketball/season/opponent.rb +15 -0
  35. data/lib/basketball/season/{season_game.rb → regular.rb} +2 -1
  36. data/lib/basketball/season/result.rb +37 -0
  37. data/lib/basketball/season.rb +12 -13
  38. data/lib/basketball/value_object.rb +4 -1
  39. data/lib/basketball/version.rb +1 -1
  40. data/lib/basketball.rb +9 -4
  41. metadata +32 -44
  42. data/lib/basketball/draft/league.rb +0 -70
  43. data/lib/basketball/draft/player.rb +0 -43
  44. data/lib/basketball/draft/room_serializer.rb +0 -186
  45. data/lib/basketball/draft/roster.rb +0 -37
  46. data/lib/basketball/draft/sim_event.rb +0 -23
  47. data/lib/basketball/draft/skip_event.rb +0 -13
  48. data/lib/basketball/season/calendar_serializer.rb +0 -94
  49. data/lib/basketball/season/conference.rb +0 -57
  50. data/lib/basketball/season/division.rb +0 -43
  51. data/lib/basketball/season/league.rb +0 -114
  52. data/lib/basketball/season/league_serializer.rb +0 -99
  53. data/lib/basketball/season/scheduling_cli.rb +0 -198
  54. data/lib/basketball/season/team.rb +0 -21
@@ -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
@@ -1,30 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'room'
4
- require_relative 'room_serializer'
5
- require_relative 'player_search'
6
- require_relative 'position'
7
-
8
3
  module Basketball
9
- module Draft
4
+ module App
10
5
  # Examples:
11
- # exe/basketball-draft -o tmp/draft.json
12
- # exe/basketball-draft -i tmp/draft.json -o tmp/draft-wip.json -s 26 -p P-5,P-10 -t 10 -q PG
13
- # exe/basketball-draft -i tmp/draft-wip.json -x 2
14
- # exe/basketball-draft -i tmp/draft-wip.json -r -t 10
15
- # exe/basketball-draft -i tmp/draft-wip.json -t 10 -q SG
16
- # exe/basketball-draft -i tmp/draft-wip.json -s 30 -t 10
17
- # exe/basketball-draft -i tmp/draft-wip.json -a -r
18
- # exe/basketball-draft -i tmp/draft-wip.json -l
19
- class CLI
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
20
15
  class PlayerNotFound < StandardError; end
21
16
 
22
- attr_reader :opts, :serializer, :io
17
+ attr_reader :opts, :io, :repository
23
18
 
24
19
  def initialize(args:, io: $stdout)
25
20
  @io = io
26
- @serializer = RoomSerializer.new
27
21
  @opts = slop_parse(args)
22
+ @repository = RoomRepository.new
28
23
 
29
24
  if opts[:input].to_s.empty? && opts[:output].to_s.empty?
30
25
  io.puts('Input and/or output paths are required.')
@@ -39,49 +34,46 @@ module Basketball
39
34
  room = load_room
40
35
 
41
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
42
47
 
48
+ def status(room)
43
49
  io.puts
44
50
  io.puts('Status')
45
51
 
46
52
  if room.done?
47
53
  io.puts('Draft is complete!')
48
54
  else
49
- current_round = room.current_round
50
- current_round_pick = room.current_round_pick
51
- current_front_office = room.current_front_office
55
+ round = room.round
56
+ round_pick = room.round_pick
57
+ front_office = room.front_office
52
58
 
53
59
  io.puts("#{room.remaining_picks} Remaining pick(s)")
54
- io.puts("Up Next: Round #{current_round} pick #{current_round_pick} for #{current_front_office}")
60
+ io.puts("Up Next: Round #{round} pick #{round_pick} for #{front_office}")
55
61
  end
56
-
57
- write(room)
58
-
59
- log(room)
60
-
61
- league(room)
62
-
63
- query(room)
64
-
65
- self
66
62
  end
67
63
 
68
- private
69
-
70
64
  def slop_parse(args)
71
65
  Slop.parse(args) do |o|
72
- o.banner = 'Usage: basketball-draft [options] ...'
66
+ o.banner = 'Usage: basketball-room [options] ...'
73
67
 
74
- o.string '-i', '--input',
75
- 'Path to load the room from. If omitted then a new draft will be generated.'
68
+ o.string '-i', '--input', 'Path to load the Room from. If omitted then a new draft will be generated.'
76
69
  o.string '-o', '--output', 'Path to write the room to (if omitted then input path will be used)'
77
70
  o.integer '-s', '--simulate', 'Number of picks to simulate (default is 0).', default: 0
78
71
  o.bool '-a', '--simulate-all', 'Simulate the rest of the draft', default: false
79
72
  o.array '-p', '--picks', 'Comma-separated list of ordered player IDs to pick.', delimiter: ','
80
73
  o.integer '-t', '--top', 'Output the top rated available players (default is 0).', default: 0
81
- o.string '-q', '--query', "Filter TOP by position: #{Position::ALL_VALUES.join(', ')}."
82
- o.bool '-r', '--rosters', 'Output all front_office rosters.', default: false
74
+ o.bool '-l', '--league', 'Output all teams and their picks', default: false
83
75
  o.integer '-x', '--skip', 'Number of picks to skip (default is 0).', default: 0
84
- o.bool '-l', '--log', 'Output event log.', default: false
76
+ o.bool '-e', '--events', 'Output event log.', default: false
85
77
 
86
78
  o.on '-h', '--help', 'Print out help, like this is doing right now.' do
87
79
  io.puts(o)
@@ -104,33 +96,31 @@ module Basketball
104
96
 
105
97
  def generate_draft
106
98
  front_offices = 30.times.map do |i|
107
- FrontOffice.new(
108
- id: "T-#{i + 1}", name: Faker::Team.name
99
+ Draft::FrontOffice.new(
100
+ id: "T-#{i + 1}"
109
101
  )
110
102
  end
111
103
 
112
104
  players = 450.times.map do |i|
113
- Player.new(
105
+ Org::Player.new(
114
106
  id: "P-#{i + 1}",
115
- first_name: Faker::Name.first_name,
116
- last_name: Faker::Name.last_name,
117
- position: Position.random,
118
- overall: (0..100).to_a.sample
107
+ overall: (20..100).to_a.sample,
108
+ position: Org::Position.random
119
109
  )
120
110
  end
121
111
 
122
- Room.new(players:, front_offices:)
112
+ Draft::Room.new(rounds: 12, players:, front_offices:)
123
113
  end
124
114
 
125
115
  def league(room)
126
- return unless opts[:rosters]
116
+ return unless opts[:league]
127
117
 
128
118
  io.puts
129
- io.puts(room.to_league)
119
+ io.puts(room.league)
130
120
  end
131
121
 
132
- def log(room)
133
- return unless opts[:log]
122
+ def events(room)
123
+ return unless opts[:events]
134
124
 
135
125
  io.puts
136
126
  io.puts('Event Log')
@@ -138,32 +128,20 @@ module Basketball
138
128
  puts room.events
139
129
  end
140
130
 
141
- # rubocop:disable Metrics/AbcSize
142
131
  def query(room)
143
132
  top = opts[:top]
144
133
 
145
134
  return if top <= 0
146
135
 
147
- search = PlayerSearch.new(room.undrafted_players)
148
- position = opts[:query].to_s.empty? ? nil : Position.new(opts[:query])
149
- players = search.query(position:).take(opts[:top])
136
+ players = room.undrafted_players.sort_by(&:overall).reverse.take(opts[:top])
150
137
 
151
138
  io.puts
152
- io.print("Top #{top} available players")
153
-
154
- if position
155
- io.puts(" for #{position} position:")
156
- else
157
- io.puts(' for all positions:')
158
- end
159
-
139
+ io.puts("Top #{top} available players")
160
140
  io.puts(players)
161
141
  end
162
- # rubocop:enable Metrics/AbcSize
163
142
 
164
143
  def read
165
- contents = File.read(opts[:input])
166
- serializer.deserialize(contents)
144
+ repository.load(opts[:input])
167
145
  end
168
146
 
169
147
  # rubocop:disable Metrics/AbcSize
@@ -195,14 +173,14 @@ module Basketball
195
173
  event_count += 1
196
174
  end
197
175
 
198
- room.sim!(opts[:simulate]) do |event|
199
- io.puts(event)
176
+ opts[:simulate].times do
177
+ room.sim!
200
178
 
201
179
  event_count += 1
202
180
  end
203
181
 
204
182
  if opts[:simulate_all]
205
- room.sim! do |event|
183
+ room.sim_rest! do |event|
206
184
  io.puts(event)
207
185
 
208
186
  event_count += 1
@@ -215,15 +193,14 @@ module Basketball
215
193
  end
216
194
  # rubocop:enable Metrics/AbcSize
217
195
 
218
- def write(room)
219
- output = opts[:output].to_s.empty? ? opts[:input] : opts[:output]
220
-
221
- contents = serializer.serialize(room)
222
- out_dir = File.dirname(output)
196
+ def output_default_to_input
197
+ opts[:output].to_s.empty? ? opts[:input] : opts[:output]
198
+ end
223
199
 
224
- FileUtils.mkdir_p(out_dir)
200
+ def write(room)
201
+ output = output_default_to_input
225
202
 
226
- File.write(output, contents)
203
+ repository.save(output, room)
227
204
 
228
205
  io.puts
229
206
  io.puts("Draft written to: #{output}")
@@ -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
@@ -2,8 +2,9 @@
2
2
 
3
3
  module Basketball
4
4
  module Draft
5
+ # Describes what all Room events have to have to be considered an "event".
5
6
  class Event < ValueObject
6
- attr_reader_value :pick, :round, :round_pick, :front_office
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} overall (R#{round}:P#{round_pick}) by #{front_office}"
21
+ "[##{pick} R:#{round} P:#{round_pick}] #{front_office}"
21
22
  end
22
23
  end
23
24
  end