basketball 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 71ab0cd4aa4e2bb10d0a9640e18a756938fbe749842abdbbda9be9f2023be9cf
4
- data.tar.gz: 5d216901cadb3c92d41a1e445956aef4a11fc1d3e418430003d050693c43d3be
3
+ metadata.gz: 793bfe7afef52aa72d3f89c94541fc7d8f24b350d63cb7c75d7215c285695b74
4
+ data.tar.gz: 46a3ac7199fb534ff4827c8fe48bbe371c7f87bc623e579ffdce2310132a1263
5
5
  SHA512:
6
- metadata.gz: a3812684e58e97711745e31a8696c12c32c79e542661302db86eb7b0fecc3784ede1650ec2d0079b66a641f5e09da657f22f596583630d932e03e55ba7ed3514
7
- data.tar.gz: f1edd69f31a2ff8ca4d5e53bab9bd59c74a091ab276db1a53901aa1e7de17202898ba01a917304f1e7018da1b3ad129fb012a3ac12516e832f4b8fa6cb8cd232
6
+ metadata.gz: cf8c46719f3669a31c932d7dc2742c287666fcb305c90c596c87b79c37d4fb95aafa446c70575ed4d8d3750a3f3672646e9ee49c6a0cf95e4dd7eb092831670e
7
+ data.tar.gz: db01d3bfc4a8c1448875c5a099d0a33fd60ad60471ae5cda5f9eeeaf8a1fb2daa3e52d5ca2d8138867e42aaf6f54d259cdad7d3628cfbaa2d7bcf134fe5dc4b9
data/CHANGELOG.md CHANGED
@@ -1,8 +1,11 @@
1
+ #### 0.0.5 - May 5th, 2023
2
+
3
+ * Remove the notion of Team in favor of a flat front office.
1
4
  #### 0.0.4 - May 5th, 2023
2
5
 
3
6
  * Add ability to skip draft picks using `Basketball::Drafting::Engine#skip!`
4
- * Add ability to output event full drafting event log using CLI: `exe/basketball-draft -i tmp/draft-wip.json -l`
5
- * Add ability to skip draft picks using CLI: `exe/basketball-draft -i tmp/draft-wip.json -x 1`
7
+ * Add ability to output event full drafting event log using CLI: `basketball-draft -i tmp/draft-wip.json -l`
8
+ * Add ability to skip draft picks using CLI: `basketball-draft -i tmp/draft-wip.json -x 1`
6
9
 
7
10
  #### 0.0.3 - May 5th, 2023
8
11
 
data/CODE_OF_CONDUCT.md CHANGED
@@ -56,7 +56,7 @@ further defined and clarified by project maintainers.
56
56
 
57
57
  Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
58
  reported. All complaints will be reviewed and investigated and will result in a response that
59
- is deemed necessary and appropriate to the circumstances. The project team is
59
+ is deemed necessary and appropriate to the circumstances. The project front_office is
60
60
  obligated to maintain confidentiality with regard to the reporter of an incident.
61
61
  Further details of specific enforcement policies may be posted separately.
62
62
 
data/README.md CHANGED
@@ -26,7 +26,7 @@ Install executable scripts:
26
26
  bundle binstubs basketball
27
27
  ````
28
28
 
29
- ## Main Modules
29
+ ## Sub-Modules
30
30
 
31
31
  This library is broken down into several bounded contexts that can be consumed either via its Ruby API's or CLI through provided executable scripts:
32
32
 
@@ -38,6 +38,21 @@ The drafting module is responsible for providing a turn-based iterator allowing
38
38
 
39
39
  ![Basketball Architecture - Drafting.png](/docs/architecture/drafting.png)
40
40
 
41
+ Element | Description
42
+ :------------ | :-----------
43
+ **Drafting** | Bounded context (sub-module) dealing with executing an asynchronous draft.
44
+ **Engine** | Aggregate root responsible for providing an iterable interface capable of executing a draft, pick by pick.
45
+ **Event** | Represents one cycle execution result from the Engine.
46
+ **External Ruby App** | An example consumer for the Drafting context.
47
+ **Front Office** | Identifiable as a team, contains configuration for how to auto-pick draft selections.
48
+ **League** | Set of rosters that together form a cohesive league.
49
+ **Pick Event** | Result event emitted when a player is manually selected.
50
+ **Playe ** | Identitiable as a person able to be drafted.
51
+ **Position** | Value object based on position code: PG, SG, SF, PF, and C.
52
+ **Roster** | Identifiable as a team, set of players that make up a single team.
53
+ **Sim Event** | Result event emitted when a player is automatically selected by a front office.
54
+ **Skip Event** | Result event emitted when a front office decides to skip a round.
55
+
41
56
  ### The Drafting CLI
42
57
 
43
58
  The drafting module is meant to be interfaces using its Ruby API by consuming applications. It also ships with a CLI which a user can interact with to emulate "the draft process". Technically speaking, the CLI provides an example application built on top of the Drafting module. Each time a CLI command is executed, its results will be resaved, so the output file can then be used as the next command's input file to string together commands. The following sections are example CLI interactions:
@@ -45,43 +60,55 @@ The drafting module is meant to be interfaces using its Ruby API by consuming ap
45
60
  #### Generate a Fresh Draft
46
61
 
47
62
  ```zsh
48
- exe/basketball-draft -o tmp/draft.json
63
+ basketball-draft -o tmp/draft.json
49
64
  ```
50
65
 
51
66
  #### N Top Available Players
52
67
 
53
68
  ```zsh
54
- exe/basketball-draft -i tmp/draft.json -t 10
69
+ basketball-draft -i tmp/draft.json -t 10
55
70
  ```
56
71
 
57
72
  #### N Top Available Players for a Position
58
73
 
59
74
  ```zsh
60
- exe/basketball-draft -i tmp/draft.json -t 10 -q PG
75
+ basketball-draft -i tmp/draft.json -t 10 -q PG
61
76
  ```
62
77
 
63
78
  #### Output Current Rosters
64
79
 
65
80
  ```zsh
66
- exe/basketball-draft -i tmp/draft.json -r
81
+ basketball-draft -i tmp/draft.json -r
82
+ ```
83
+
84
+ #### Output Event Log
85
+
86
+ ```zsh
87
+ basketball-draft -i tmp/draft.json -l
67
88
  ```
68
89
 
69
90
  #### Simulate N picks
70
91
 
71
92
  ```zsh
72
- exe/basketball-draft -i tmp/draft.json -s 10
93
+ basketball-draft -i tmp/draft.json -s 10
94
+ ```
95
+
96
+ #### Skip N picks
97
+
98
+ ```zsh
99
+ basketball-draft -i tmp/draft.json -x 10
73
100
  ```
74
101
 
75
102
  #### Pick Players
76
103
 
77
104
  ```zsh
78
- exe/basketball-draft -i tmp/draft.json -p P-100,P-200,P-300
105
+ basketball-draft -i tmp/draft.json -p P-100,P-200,P-300
79
106
  ```
80
107
 
81
108
  #### Simulate the Rest of the Draft
82
109
 
83
110
  ```zsh
84
- exe/basketball-draft -i tmp/draft.json -a
111
+ basketball-draft -i tmp/draft.json -a
85
112
  ```
86
113
 
87
114
  ## Contributing
@@ -46,15 +46,19 @@ module Basketball
46
46
  if engine.done?
47
47
  io.puts('Draft is complete!')
48
48
  else
49
+ current_round = engine.current_round
50
+ current_round_pick = engine.current_round_pick
51
+ current_front_office = engine.current_front_office
52
+
49
53
  io.puts("#{engine.remaining_picks} Remaining pick(s)")
50
- io.puts("Up Next: Round #{engine.current_round} pick #{engine.current_round_pick} for #{engine.current_team}")
54
+ io.puts("Up Next: Round #{current_round} pick #{current_round_pick} for #{current_front_office}")
51
55
  end
52
56
 
53
57
  write(engine)
54
58
 
55
59
  log(engine)
56
60
 
57
- rosters(engine)
61
+ league(engine)
58
62
 
59
63
  query(engine)
60
64
 
@@ -75,7 +79,7 @@ module Basketball
75
79
  o.array '-p', '--picks', 'Comma-separated list of ordered player IDs to pick.', delimiter: ','
76
80
  o.integer '-t', '--top', 'Output the top rated available players (default is 0).', default: 0
77
81
  o.string '-q', '--query', "Filter TOP by position: #{Position::ALL_VALUES.join(', ')}."
78
- o.bool '-r', '--rosters', 'Output all team rosters.', default: false
82
+ o.bool '-r', '--rosters', 'Output all front_office rosters.', default: false
79
83
  o.integer '-x', '--skip', 'Number of picks to skip (default is 0).', default: 0
80
84
  o.bool '-l', '--log', 'Output event log.', default: false
81
85
 
@@ -88,7 +92,7 @@ module Basketball
88
92
 
89
93
  def load_engine
90
94
  if opts[:input].to_s.empty?
91
- io.puts('Input path was not provided, generating fresh teams and players')
95
+ io.puts('Input path was not provided, generating fresh front_offices and players')
92
96
 
93
97
  generate_draft
94
98
  else
@@ -99,8 +103,8 @@ module Basketball
99
103
  end
100
104
 
101
105
  def generate_draft
102
- teams = 30.times.map do |i|
103
- Team.new(
106
+ front_offices = 30.times.map do |i|
107
+ FrontOffice.new(
104
108
  id: "T-#{i + 1}", name: Faker::Team.name
105
109
  )
106
110
  end
@@ -115,18 +119,14 @@ module Basketball
115
119
  )
116
120
  end
117
121
 
118
- Engine.new(players:, teams:)
122
+ Engine.new(players:, front_offices:)
119
123
  end
120
124
 
121
- def rosters(engine)
125
+ def league(engine)
122
126
  return unless opts[:rosters]
123
127
 
124
128
  io.puts
125
- io.puts('Rosters')
126
-
127
- engine.rosters.each do |roster|
128
- io.puts(roster)
129
- end
129
+ io.puts(engine.to_league)
130
130
  end
131
131
 
132
132
  def log(engine)
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'roster'
3
+ require_relative 'league'
4
4
 
5
5
  module Basketball
6
6
  module Drafting
@@ -9,7 +9,7 @@ module Basketball
9
9
  class DupeEventError < StandardError; end
10
10
  class EventOutOfOrderError < StandardError; end
11
11
  class UnknownPlayerError < StandardError; end
12
- class UnknownTeamError < StandardError; end
12
+ class UnknownFrontOfficeError < StandardError; end
13
13
  class EndOfDraftError < StandardError; end
14
14
 
15
15
  DEFAULT_ROUNDS = 12
@@ -18,11 +18,11 @@ module Basketball
18
18
 
19
19
  attr_reader :events, :rounds
20
20
 
21
- def initialize(players: [], teams: [], events: [], rounds: DEFAULT_ROUNDS)
22
- @players_by_id = players.to_h { |p| [p.id, p] }
23
- @teams_by_id = teams.to_h { |t| [t.id, t] }
24
- @events = []
25
- @rounds = rounds.to_i
21
+ def initialize(players: [], front_offices: [], events: [], rounds: DEFAULT_ROUNDS)
22
+ @players_by_id = players.to_h { |p| [p.id, p] }
23
+ @front_offices_by_id = front_offices.to_h { |fo| [fo.id, fo] }
24
+ @events = []
25
+ @rounds = rounds.to_i
26
26
 
27
27
  # Each one will be validated for correctness.
28
28
  events.each { |e| play!(e) }
@@ -30,15 +30,15 @@ module Basketball
30
30
  freeze
31
31
  end
32
32
 
33
- def rosters
34
- events_by_team = teams.to_h { |t| [t, []] }
33
+ def to_league
34
+ League.new(front_offices:).tap do |league|
35
+ player_events.each do |event|
36
+ league.register!(player: event.player, front_office: event.front_office)
37
+ end
35
38
 
36
- events.each do |event|
37
- events_by_team.fetch(event.team) << event
38
- end
39
-
40
- events_by_team.map do |team, events|
41
- Roster.new(team:, events:)
39
+ undrafted_players.each do |player|
40
+ league.register!(player:)
41
+ end
42
42
  end
43
43
  end
44
44
 
@@ -46,8 +46,8 @@ module Basketball
46
46
  events.join("\n")
47
47
  end
48
48
 
49
- def teams
50
- teams_by_id.values
49
+ def front_offices
50
+ front_offices_by_id.values
51
51
  end
52
52
 
53
53
  def players
@@ -55,27 +55,27 @@ module Basketball
55
55
  end
56
56
 
57
57
  def total_picks
58
- rounds * teams.length
58
+ rounds * front_offices.length
59
59
  end
60
60
 
61
61
  def current_round
62
62
  return if done?
63
63
 
64
- (current_pick / teams.length.to_f).ceil
64
+ (current_pick / front_offices.length.to_f).ceil
65
65
  end
66
66
 
67
67
  def current_round_pick
68
68
  return if done?
69
69
 
70
- mod = current_pick % teams.length
70
+ mod = current_pick % front_offices.length
71
71
 
72
- mod.positive? ? mod : teams.length
72
+ mod.positive? ? mod : front_offices.length
73
73
  end
74
74
 
75
- def current_team
75
+ def current_front_office
76
76
  return if done?
77
77
 
78
- teams[current_round_pick - 1]
78
+ front_offices[current_round_pick - 1]
79
79
  end
80
80
 
81
81
  def current_pick
@@ -101,7 +101,7 @@ module Basketball
101
101
 
102
102
  event = SkipEvent.new(
103
103
  id: SecureRandom.uuid,
104
- team: current_team,
104
+ front_office: current_front_office,
105
105
  pick: current_pick,
106
106
  round: current_round,
107
107
  round_pick: current_round_pick
@@ -117,17 +117,17 @@ module Basketball
117
117
  events = []
118
118
 
119
119
  until done? || (times && counter >= times)
120
- team = current_team
120
+ front_office = current_front_office
121
121
 
122
- player = team.pick(
122
+ player = front_office.pick(
123
123
  undrafted_player_search:,
124
- drafted_players: drafted_players(team),
124
+ drafted_players: drafted_players(front_office),
125
125
  round: current_round
126
126
  )
127
127
 
128
128
  event = SimEvent.new(
129
129
  id: SecureRandom.uuid,
130
- team:,
130
+ front_office:,
131
131
  player:,
132
132
  pick: current_pick,
133
133
  round: current_round,
@@ -150,7 +150,7 @@ module Basketball
150
150
 
151
151
  event = PickEvent.new(
152
152
  id: SecureRandom.uuid,
153
- team: current_team,
153
+ front_office: current_front_office,
154
154
  player:,
155
155
  pick: current_pick,
156
156
  round: current_round,
@@ -170,7 +170,7 @@ module Basketball
170
170
 
171
171
  private
172
172
 
173
- attr_reader :players_by_id, :teams_by_id
173
+ attr_reader :players_by_id, :front_offices_by_id
174
174
 
175
175
  def player_events
176
176
  events.select { |e| e.respond_to?(:player) }
@@ -180,9 +180,9 @@ module Basketball
180
180
  events.length + 1
181
181
  end
182
182
 
183
- def drafted_players(team = nil)
183
+ def drafted_players(front_office = nil)
184
184
  player_events.each_with_object([]) do |e, memo|
185
- next unless team.nil? || e.team == team
185
+ next unless front_office.nil? || e.front_office == front_office
186
186
 
187
187
  memo << e.player
188
188
  end
@@ -200,13 +200,17 @@ module Basketball
200
200
  raise UnknownPlayerError, "#{event.player} doesnt exist"
201
201
  end
202
202
 
203
- raise DupeEventError, "#{event} is a dupe" if events.include?(event)
204
- raise EventOutOfOrder, "#{event} team cant pick right now" if event.team != current_team
205
- raise EventOutOfOrder, "#{event} has wrong pick" if event.pick != current_pick
206
- raise EventOutOfOrder, "#{event} has wrong round" if event.round != current_round
207
- raise EventOutOfOrder, "#{event} has wrong round_pick" if event.round_pick != current_round_pick
208
- raise UnknownTeamError, "#{team} doesnt exist" unless teams.include?(event.team)
209
- raise EndOfDraftError, "#{total_picks} pick limit reached" if events.length > total_picks + 1
203
+ if event.front_office != current_front_office
204
+ raise EventOutOfOrder, "#{event} #{event.front_office} cant pick right now"
205
+ end
206
+
207
+ raise UnknownFrontOfficeError, "#{front_office} doesnt exist" unless front_offices.include?(event.front_office)
208
+
209
+ raise DupeEventError, "#{event} is a dupe" if events.include?(event)
210
+ raise EventOutOfOrder, "#{event} has wrong pick" if event.pick != current_pick
211
+ raise EventOutOfOrder, "#{event} has wrong round" if event.round != current_round
212
+ raise EventOutOfOrder, "#{event} has wrong round_pick" if event.round_pick != current_round_pick
213
+ raise EndOfDraftError, "#{total_picks} pick limit reached" if events.length > total_picks + 1
210
214
 
211
215
  events << event
212
216
 
@@ -2,7 +2,6 @@
2
2
 
3
3
  require_relative 'front_office'
4
4
  require_relative 'player'
5
- require_relative 'team'
6
5
  require_relative 'pick_event'
7
6
  require_relative 'sim_event'
8
7
  require_relative 'skip_event'
@@ -19,14 +18,14 @@ module Basketball
19
18
  private_constant :EVENT_CLASSES
20
19
 
21
20
  def deserialize(string)
22
- json = JSON.parse(string, symbolize_names: true)
23
- teams = deserialize_teams(json)
24
- players = deserialize_players(json)
25
- events = deserialize_events(json, players, teams)
21
+ json = JSON.parse(string, symbolize_names: true)
22
+ front_offices = deserialize_front_offices(json)
23
+ players = deserialize_players(json)
24
+ events = deserialize_events(json, players, front_offices)
26
25
 
27
26
  engine_opts = {
28
27
  players:,
29
- teams:,
28
+ front_offices:,
30
29
  events:
31
30
  }
32
31
 
@@ -39,7 +38,7 @@ module Basketball
39
38
  {
40
39
  info: serialize_info(engine),
41
40
  engine: serialize_engine(engine),
42
- rosters: serialize_rosters(engine)
41
+ league: serialize_league(engine)
43
42
  }.to_json
44
43
  end
45
44
 
@@ -48,7 +47,7 @@ module Basketball
48
47
  def serialize_engine(engine)
49
48
  {
50
49
  rounds: engine.rounds,
51
- teams: serialize_teams(engine),
50
+ front_offices: serialize_front_offices(engine),
52
51
  players: serialize_players(engine),
53
52
  events: serialize_events(engine.events)
54
53
  }
@@ -59,37 +58,40 @@ module Basketball
59
58
  total_picks: engine.total_picks,
60
59
  current_round: engine.current_round,
61
60
  current_round_pick: engine.current_round_pick,
62
- current_team: engine.current_team&.id,
61
+ current_front_office: engine.current_front_office&.id,
63
62
  current_pick: engine.current_pick,
64
63
  remaining_picks: engine.remaining_picks,
65
- done: engine.done?,
66
- undrafted_players: engine.undrafted_players.map(&:id)
64
+ done: engine.done?
67
65
  }
68
66
  end
69
67
 
70
- def serialize_rosters(engine)
71
- engine.rosters.to_h do |roster|
68
+ def serialize_league(engine)
69
+ league = engine.to_league
70
+
71
+ rosters = league.rosters.to_h do |roster|
72
72
  [
73
73
  roster.id,
74
74
  {
75
- events: roster.events.map(&:id),
76
75
  players: roster.players.map(&:id)
77
76
  }
78
77
  ]
79
78
  end
79
+
80
+ {
81
+ free_agents: league.free_agents.map(&:id),
82
+ rosters:
83
+ }
80
84
  end
81
85
 
82
- def serialize_teams(engine)
83
- engine.teams.to_h do |team|
86
+ def serialize_front_offices(engine)
87
+ engine.front_offices.to_h do |front_office|
84
88
  [
85
- team.id,
89
+ front_office.id,
86
90
  {
87
- name: team.name,
88
- front_office: {
89
- fuzz: team.front_office.fuzz,
90
- depth: team.front_office.depth,
91
- prioritized_positions: team.front_office.prioritized_positions
92
- }
91
+ name: front_office.name,
92
+ fuzz: front_office.fuzz,
93
+ depth: front_office.depth,
94
+ prioritized_positions: front_office.prioritized_positions.map(&:code)
93
95
  }
94
96
  ]
95
97
  end
@@ -114,7 +116,7 @@ module Basketball
114
116
  {
115
117
  type: event.class.name.split('::').last,
116
118
  id: event.id,
117
- team: event.team.id,
119
+ front_office: event.front_office.id,
118
120
  pick: event.pick,
119
121
  round: event.round,
120
122
  round_pick: event.round_pick
@@ -124,30 +126,21 @@ module Basketball
124
126
  end
125
127
  end
126
128
 
127
- def deserialize_teams(json)
128
- (json.dig(:engine, :teams) || []).map do |id, team_hash|
129
- team_opts = {
129
+ def deserialize_front_offices(json)
130
+ (json.dig(:engine, :front_offices) || []).map do |id, front_office_hash|
131
+ prioritized_positions = (front_office_hash[:prioritized_positions] || []).map do |v|
132
+ Position.new(v)
133
+ end
134
+
135
+ front_office_opts = {
130
136
  id:,
131
- name: team_hash[:name]
137
+ name: front_office_hash[:name],
138
+ prioritized_positions:,
139
+ fuzz: front_office_hash[:fuzz],
140
+ depth: front_office_hash[:depth]
132
141
  }
133
142
 
134
- if team_hash.key?(:front_office)
135
- front_office_hash = team_hash[:front_office] || {}
136
-
137
- prioritized_positions = (front_office_hash[:prioritized_positions] || []).map do |v|
138
- Position.new(v)
139
- end
140
-
141
- front_office_opts = {
142
- prioritized_positions:,
143
- fuzz: front_office_hash[:fuzz],
144
- depth: front_office_hash[:depth]
145
- }
146
-
147
- team_opts[:front_office] = FrontOffice.new(**front_office_opts)
148
- end
149
-
150
- Team.new(**team_opts)
143
+ FrontOffice.new(**front_office_opts)
151
144
  end
152
145
  end
153
146
 
@@ -162,10 +155,10 @@ module Basketball
162
155
  end
163
156
  end
164
157
 
165
- def deserialize_events(json, players, teams)
158
+ def deserialize_events(json, players, front_offices)
166
159
  (json.dig(:engine, :events) || []).map do |event_hash|
167
160
  event_opts = event_hash.slice(:id, :pick, :round, :round_pick).merge(
168
- team: teams.find { |t| t.id == event_hash[:team] }
161
+ front_office: front_offices.find { |t| t.id == event_hash[:front_office] }
169
162
  )
170
163
 
171
164
  class_constant = EVENT_CLASSES.fetch(event_hash[:type])
@@ -3,21 +3,21 @@
3
3
  module Basketball
4
4
  module Drafting
5
5
  class Event < Entity
6
- attr_reader :pick, :round, :round_pick, :team
6
+ attr_reader :pick, :round, :round_pick, :front_office
7
7
 
8
- def initialize(id:, team:, pick:, round:, round_pick:)
8
+ def initialize(id:, front_office:, pick:, round:, round_pick:)
9
9
  super(id)
10
10
 
11
- raise ArgumentError, 'team required' unless team
11
+ raise ArgumentError, 'front_office required' unless front_office
12
12
 
13
- @team = team
14
- @pick = pick.to_i
15
- @round = round.to_i
16
- @round_pick = round_pick.to_i
13
+ @front_office = front_office
14
+ @pick = pick.to_i
15
+ @round = round.to_i
16
+ @round_pick = round_pick.to_i
17
17
  end
18
18
 
19
19
  def to_s
20
- "##{pick} overall (R#{round}:P#{round_pick}) by #{team}"
20
+ "##{pick} overall (R#{round}:P#{round_pick}) by #{front_office}"
21
21
  end
22
22
  end
23
23
  end
@@ -2,18 +2,19 @@
2
2
 
3
3
  module Basketball
4
4
  module Drafting
5
- class FrontOffice < ValueObject
5
+ class FrontOffice < Entity
6
6
  MAX_DEPTH = 3
7
7
  MAX_FUZZ = 2
8
8
  MAX_POSITIONS = 12
9
9
 
10
10
  private_constant :MAX_DEPTH, :MAX_FUZZ, :MAX_POSITIONS
11
11
 
12
- attr_reader_value :prioritized_positions, :fuzz, :depth
12
+ attr_reader :prioritized_positions, :fuzz, :depth, :name
13
13
 
14
- def initialize(prioritized_positions: [], fuzz: rand(0..MAX_FUZZ), depth: rand(0..MAX_DEPTH))
15
- super()
14
+ def initialize(id:, name: '', prioritized_positions: [], fuzz: rand(0..MAX_FUZZ), depth: rand(0..MAX_DEPTH))
15
+ super(id)
16
16
 
17
+ @name = name
17
18
  @fuzz = fuzz.to_i
18
19
  @depth = depth.to_i
19
20
  @prioritized_positions = prioritized_positions
@@ -36,6 +37,10 @@ module Basketball
36
37
  players[0..fuzz].sample
37
38
  end
38
39
 
40
+ def to_s
41
+ "[#{super}] #{name}"
42
+ end
43
+
39
44
  private
40
45
 
41
46
  def adaptive_search(undrafted_player_search:, drafted_players:)
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'roster'
4
+
5
+ module Basketball
6
+ module Drafting
7
+ class League
8
+ class PlayerAlreadyRegisteredError < StandardError; end
9
+ class RosterNotFoundError < StandardError; end
10
+ class RosterAlreadyAddedError < StandardError; end
11
+
12
+ attr_reader :free_agents, :rosters
13
+
14
+ def initialize(free_agents: [], front_offices: [])
15
+ @rosters = []
16
+ @free_agents = []
17
+
18
+ front_offices.each { |front_office| add_roster(front_office) }
19
+ free_agents.each { |p| register!(player: p) }
20
+
21
+ freeze
22
+ end
23
+
24
+ def roster(front_office)
25
+ rosters.find { |r| r == front_office }
26
+ end
27
+
28
+ def register!(player:, front_office: nil)
29
+ raise PlayerRequiredError, 'player is required' unless player
30
+
31
+ rosters.each do |roster|
32
+ if roster.registered?(player)
33
+ raise PlayerAlreadyRegisteredError,
34
+ "#{player} already registered to: #{roster.id}"
35
+ end
36
+ end
37
+
38
+ if free_agents.include?(player)
39
+ raise PlayerAlreadyRegisteredError,
40
+ "#{player} already registered as a free agent"
41
+ end
42
+
43
+ if front_office
44
+ roster = roster(front_office)
45
+
46
+ raise RosterNotFoundError, "Roster not found for: #{front_office}" unless roster
47
+
48
+ roster.sign!(player)
49
+ else
50
+ free_agents << player
51
+ end
52
+
53
+ self
54
+ end
55
+
56
+ def to_s
57
+ (['League'] + rosters.map(&:to_s)).join("\n")
58
+ end
59
+
60
+ private
61
+
62
+ def add_roster(front_office)
63
+ raise RosterAlreadyAddedError, "#{front_office} already added" if rosters.include?(front_office)
64
+
65
+ rosters << Roster.new(id: front_office.id, name: front_office.name)
66
+
67
+ self
68
+ end
69
+ end
70
+ end
71
+ end
@@ -7,8 +7,8 @@ module Basketball
7
7
  class PickEvent < Event
8
8
  attr_reader :player
9
9
 
10
- def initialize(id:, team:, player:, pick:, round:, round_pick:)
11
- super(id:, team:, pick:, round:, round_pick:)
10
+ def initialize(id:, front_office:, player:, pick:, round:, round_pick:)
11
+ super(id:, front_office:, pick:, round:, round_pick:)
12
12
 
13
13
  raise ArgumentError, 'player required' unless player
14
14
 
@@ -2,41 +2,36 @@
2
2
 
3
3
  module Basketball
4
4
  module Drafting
5
- class Roster < ValueObject
6
- extend Forwardable
5
+ class Roster < Entity
6
+ class PlayerAlreadyRegisteredError < StandardError; end
7
+ class PlayerRequiredError < StandardError; end
7
8
 
8
- class WrongTeamEventError < StandardError; end
9
+ attr_reader :name, :players
9
10
 
10
- attr_reader_value :team, :events
11
+ def initialize(id:, name: '', players: [])
12
+ super(id)
11
13
 
12
- def_delegators :team, :id
14
+ @name = name.to_s
15
+ @players = players.each { |p| register!(p) }
13
16
 
14
- def initialize(team:, events: [])
15
- super()
16
-
17
- raise ArgumentError, 'team is required' unless team
18
-
19
- other_teams_pick_event_ids = events.reject { |e| e.team == team }.map(&:id)
20
-
21
- if other_teams_pick_event_ids.any?
22
- raise WrongTeamEventError,
23
- "Event(s): #{other_teams_pick_event_ids.join(',')} has wrong team"
24
- end
25
-
26
- @team = team
27
- @events = events
17
+ freeze
28
18
  end
29
19
 
30
- def player_events
31
- events.select { |e| e.respond_to?(:player) }
20
+ def registered?(player)
21
+ players.include?(player)
32
22
  end
33
23
 
34
- def players
35
- player_events.map(&:player)
24
+ def sign!(player)
25
+ raise PlayerRequiredError, 'player is required' unless player
26
+ raise PlayerAlreadyRegisteredError, "#{player} already registered for #{id}" if registered?(player)
27
+
28
+ players << player
29
+
30
+ self
36
31
  end
37
32
 
38
33
  def to_s
39
- ([team.to_s] + players.map(&:to_s)).join("\n")
34
+ (["[#{super}] #{name} Roster"] + players.map(&:to_s)).join("\n")
40
35
  end
41
36
  end
42
37
  end
@@ -5,8 +5,8 @@ module Basketball
5
5
  class SimEvent < Event
6
6
  attr_reader :player
7
7
 
8
- def initialize(id:, team:, player:, pick:, round:, round_pick:)
9
- super(id:, team:, pick:, round:, round_pick:)
8
+ def initialize(id:, front_office:, player:, pick:, round:, round_pick:)
9
+ super(id:, front_office:, pick:, round:, round_pick:)
10
10
 
11
11
  raise ArgumentError, 'player required' unless player
12
12
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Basketball
4
- VERSION = '0.0.4'
4
+ VERSION = '0.0.5'
5
5
  end
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
4
+ version: 0.0.5
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-05-05 00:00:00.000000000 Z
11
+ date: 2023-05-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faker
@@ -209,6 +209,7 @@ files:
209
209
  - lib/basketball/drafting/engine_serializer.rb
210
210
  - lib/basketball/drafting/event.rb
211
211
  - lib/basketball/drafting/front_office.rb
212
+ - lib/basketball/drafting/league.rb
212
213
  - lib/basketball/drafting/pick_event.rb
213
214
  - lib/basketball/drafting/player.rb
214
215
  - lib/basketball/drafting/player_search.rb
@@ -216,7 +217,6 @@ files:
216
217
  - lib/basketball/drafting/roster.rb
217
218
  - lib/basketball/drafting/sim_event.rb
218
219
  - lib/basketball/drafting/skip_event.rb
219
- - lib/basketball/drafting/team.rb
220
220
  - lib/basketball/entity.rb
221
221
  - lib/basketball/value_object.rb
222
222
  - lib/basketball/version.rb
@@ -1,28 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Basketball
4
- module Drafting
5
- class Team < Entity
6
- extend Forwardable
7
-
8
- attr_reader :name, :front_office
9
-
10
- def_delegators :front_office, :pick
11
-
12
- def initialize(id:, name: '', front_office: FrontOffice.new)
13
- super(id)
14
-
15
- raise ArgumentError, 'front_office is required' unless front_office
16
-
17
- @name = name.to_s
18
- @front_office = front_office
19
-
20
- freeze
21
- end
22
-
23
- def to_s
24
- "[#{super}] #{name}"
25
- end
26
- end
27
- end
28
- end