basketball 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: eade2dd71d031c05eaf7e5fa792056d9ee6621f6168c6cbd288e5106f0fe4c3a
4
- data.tar.gz: 66b92eb67dd64d84288dc5869b2076753d043311e22d903d4d8e5468ea90f7f6
3
+ metadata.gz: 29805ef216312c25dee77d7f7c2a82bf220287404e9e241d24e16dddfb895fcc
4
+ data.tar.gz: 3d8a0038acf62e4e1aa230299cc1b2d7d777596bc133b159b1e661639c75c76d
5
5
  SHA512:
6
- metadata.gz: 72a89afec17d978e79dd979a50188c62c19532a77143312a79f387a182cab5988a7e90399be60f019ce3a5d6639f5eb0ddc3f14ca896e5f3c3496488be3427de
7
- data.tar.gz: 4f04e99ddd1e64ad893395264c5753b975f4c46b741324d53bd125c1f2a8daa2079c44e2a85fd52bf6dd24d7b09fcf29c5f9266db4c67da19e8f952884de8a72
6
+ metadata.gz: ae3baac2215dbfee6b9cf70a44b2bdf9257d7dade811c6a30bf37b22f840bd85621f6441d137e07f38b615210092d0cede011a2e9f4655aa1e05c6281411a856
7
+ data.tar.gz: 4bcb67de94178f2945376faa2cb57c5f5884d60b24479d73e12d1145c7c20c1b9f3dd547f321c578f39d43868fb2f8d158859020b14d7e436fbface8a89b2809
data/CHANGELOG.md CHANGED
@@ -1,4 +1,9 @@
1
- #### 0.0.1 - May 4th, 2023
1
+ #### 0.0.3 - May 5th, 2023
2
+
3
+ * Drafting::Engine#sim! should return events
4
+ * Added Drafting::Engine#undrafted_player_search
5
+
6
+ #### 0.0.2 - May 4th, 2023
2
7
 
3
8
  * Remove autoloading in favor of require statements.
4
9
 
@@ -98,12 +98,13 @@ module Basketball
98
98
 
99
99
  def sim!(times = nil)
100
100
  counter = 0
101
+ events = []
101
102
 
102
103
  until done? || (times && counter >= times)
103
104
  team = current_team
104
105
 
105
106
  player = team.pick(
106
- undrafted_players:,
107
+ undrafted_player_search:,
107
108
  drafted_players: drafted_players(team),
108
109
  round: current_round
109
110
  )
@@ -122,9 +123,10 @@ module Basketball
122
123
  yield(event) if block_given?
123
124
 
124
125
  counter += 1
126
+ events << event
125
127
  end
126
128
 
127
- self
129
+ events
128
130
  end
129
131
 
130
132
  def pick!(player)
@@ -146,6 +148,10 @@ module Basketball
146
148
  players - drafted_players
147
149
  end
148
150
 
151
+ def undrafted_player_search
152
+ PlayerSearch.new(undrafted_players)
153
+ end
154
+
149
155
  private
150
156
 
151
157
  attr_reader :players_by_id, :teams_by_id
@@ -101,7 +101,7 @@ module Basketball
101
101
  first_name: player.first_name,
102
102
  last_name: player.last_name,
103
103
  overall: player.overall,
104
- position: player.position.value
104
+ position: player.position.code
105
105
  }
106
106
  ]
107
107
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'entity'
4
-
5
3
  module Basketball
6
4
  module Drafting
7
5
  class Event < Entity
@@ -2,16 +2,18 @@
2
2
 
3
3
  module Basketball
4
4
  module Drafting
5
- class FrontOffice
5
+ class FrontOffice < ValueObject
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 :prioritized_positions, :fuzz, :depth
12
+ attr_reader_value :prioritized_positions, :fuzz, :depth
13
13
 
14
14
  def initialize(prioritized_positions: [], fuzz: rand(0..MAX_FUZZ), depth: rand(0..MAX_DEPTH))
15
+ super()
16
+
15
17
  @fuzz = fuzz.to_i
16
18
  @depth = depth.to_i
17
19
  @prioritized_positions = prioritized_positions
@@ -24,44 +26,36 @@ module Basketball
24
26
  freeze
25
27
  end
26
28
 
27
- def to_s
28
- "#{prioritized_positions.map(&:to_s).join(',')} (F:#{fuzz} D:#{depth})"
29
- end
30
-
31
- def pick(undrafted_players:, drafted_players:, round:)
29
+ def pick(undrafted_player_search:, drafted_players:, round:)
32
30
  players = []
33
31
 
34
- players = adaptive_search(undrafted_players:, drafted_players:) if depth >= round
35
- players = balanced_search(undrafted_players:, drafted_players:) if players.empty?
36
- players = top_players(undrafted_players:) if players.empty?
32
+ players = adaptive_search(undrafted_player_search:, drafted_players:) if depth >= round
33
+ players = balanced_search(undrafted_player_search:, drafted_players:) if players.empty?
34
+ players = top_players(undrafted_player_search:) if players.empty?
37
35
 
38
36
  players[0..fuzz].sample
39
37
  end
40
38
 
41
39
  private
42
40
 
43
- def adaptive_search(undrafted_players:, drafted_players:)
44
- search = PlayerSearch.new(undrafted_players)
45
-
41
+ def adaptive_search(undrafted_player_search:, drafted_players:)
46
42
  drafted_positions = drafted_players.map(&:position)
47
43
 
48
- search.query(exclude_positions: drafted_positions)
44
+ undrafted_player_search.query(exclude_positions: drafted_positions)
49
45
  end
50
46
 
51
- def balanced_search(undrafted_players:, drafted_players:)
52
- search = PlayerSearch.new(undrafted_players)
53
-
47
+ def balanced_search(undrafted_player_search:, drafted_players:)
54
48
  players = []
55
49
 
56
50
  # Try to find best pick for exact desired position.
57
51
  # If you cant find one, then move to the next desired position until the end of the queue
58
52
  available_prioritized_positions(drafted_players:).each do |position|
59
- players = search.query(position:)
53
+ players = undrafted_player_search.query(position:)
60
54
 
61
55
  break if players.any?
62
56
  end
63
57
 
64
- players = players.any? ? players : search.query
58
+ players = players.any? ? players : undrafted_player_search.query
65
59
  end
66
60
 
67
61
  def all_random_positions
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'entity'
4
-
5
3
  module Basketball
6
4
  module Drafting
7
5
  class Player < Entity
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Basketball
4
4
  module Drafting
5
- class Position
5
+ class Position < ValueObject
6
6
  extend Forwardable
7
7
 
8
8
  class << self
@@ -17,25 +17,18 @@ module Basketball
17
17
  FRONT_COURT_VALUES = %w[PF C].to_set.freeze
18
18
  ALL_VALUES = (BACK_COURT_VALUES.to_a + FRONT_COURT_VALUES.to_a).to_set.freeze
19
19
 
20
- attr_reader :value
20
+ attr_reader_value :code
21
21
 
22
- def_delegators :value, :to_s
22
+ def_delegators :code, :to_s
23
23
 
24
- def initialize(value)
25
- @value = value.to_s.upcase
24
+ def initialize(code)
25
+ super()
26
26
 
27
- raise InvalidPositionError, "Unknown position value: #{@value}" unless ALL_VALUES.include?(@value)
27
+ @code = code.to_s.upcase
28
28
 
29
- freeze
30
- end
29
+ raise InvalidPositionError, "Unknown position code: #{@code}" unless ALL_VALUES.include?(@code)
31
30
 
32
- def ==(other)
33
- value == other.value
34
- end
35
- alias eql? ==
36
-
37
- def hash
38
- value.hash
31
+ freeze
39
32
  end
40
33
  end
41
34
  end
@@ -2,16 +2,18 @@
2
2
 
3
3
  module Basketball
4
4
  module Drafting
5
- class Roster
5
+ class Roster < ValueObject
6
6
  extend Forwardable
7
7
 
8
8
  class WrongTeamEventError < StandardError; end
9
9
 
10
- attr_reader :team, :events
10
+ attr_reader_value :team, :events
11
11
 
12
12
  def_delegators :team, :id
13
13
 
14
14
  def initialize(team:, events: [])
15
+ super()
16
+
15
17
  raise ArgumentError, 'team is required' unless team
16
18
 
17
19
  other_teams_pick_event_ids = events.reject { |e| e.team == team }.map(&:id)
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'event'
4
-
5
3
  module Basketball
6
4
  module Drafting
7
5
  class SimEvent < Event
@@ -1,12 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'entity'
4
-
5
3
  module Basketball
6
4
  module Drafting
7
5
  class Team < Entity
6
+ extend Forwardable
7
+
8
8
  attr_reader :name, :front_office
9
9
 
10
+ def_delegators :front_office, :pick
11
+
10
12
  def initialize(id:, name: '', front_office: FrontOffice.new)
11
13
  super(id)
12
14
 
@@ -21,10 +23,6 @@ module Basketball
21
23
  def to_s
22
24
  "[#{super}] #{name}"
23
25
  end
24
-
25
- def pick(undrafted_players:, drafted_players:, round:)
26
- front_office.pick(undrafted_players:, drafted_players:, round:)
27
- end
28
26
  end
29
27
  end
30
28
  end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Basketball
4
+ class Entity
5
+ extend Forwardable
6
+ include Comparable
7
+
8
+ attr_reader :id
9
+
10
+ def_delegators :id, :to_s
11
+
12
+ def initialize(id)
13
+ raise ArgumentError, 'id is required' if id.to_s.empty?
14
+
15
+ @id = id.to_s.upcase
16
+ end
17
+
18
+ def <=>(other)
19
+ id <=> other.id
20
+ end
21
+
22
+ def ==(other)
23
+ id == other.id
24
+ end
25
+ alias eql? ==
26
+
27
+ def hash
28
+ id.hash
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Basketball
4
+ class ValueObject
5
+ include Comparable
6
+
7
+ # NOTE: This current implementation most likely does not work for deep inheritance trees.
8
+ class << self
9
+ def value_keys
10
+ @value_keys ||= []
11
+ end
12
+
13
+ def sorted_value_keys
14
+ value_keys.sort
15
+ end
16
+
17
+ def attr_reader_value(*keys)
18
+ keys.each { |k| value_keys << k.to_sym }
19
+
20
+ attr_reader(*keys)
21
+ end
22
+ end
23
+
24
+ def to_s
25
+ to_h.map { |k, v| "#{k}: #{v}" }.join(', ')
26
+ end
27
+
28
+ def to_h
29
+ self.class.sorted_value_keys.to_h { |k| [k, send(k)] }
30
+ end
31
+
32
+ def [](key)
33
+ to_h[key]
34
+ end
35
+
36
+ def <=>(other)
37
+ all_sorted_values <=> other.all_sorted_values
38
+ end
39
+
40
+ def ==(other)
41
+ to_h == other.to_h
42
+ end
43
+ alias eql? ==
44
+
45
+ def hash
46
+ all_sorted_values.map(&:hash).hash
47
+ end
48
+
49
+ def all_sorted_values
50
+ self.class.sorted_value_keys.map { |k| self[k] }
51
+ end
52
+ end
53
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Basketball
4
- VERSION = '0.0.2'
4
+ VERSION = '0.0.3'
5
5
  end
data/lib/basketball.rb CHANGED
@@ -7,4 +7,9 @@ require 'json'
7
7
  require 'securerandom'
8
8
  require 'slop'
9
9
 
10
+ # Top-level
11
+ require_relative 'basketball/entity'
12
+ require_relative 'basketball/value_object'
13
+
14
+ # Submodules
10
15
  require_relative 'basketball/drafting'
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.2
4
+ version: 0.0.3
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-04 00:00:00.000000000 Z
11
+ date: 2023-05-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faker
@@ -207,7 +207,6 @@ files:
207
207
  - lib/basketball/drafting/cli.rb
208
208
  - lib/basketball/drafting/engine.rb
209
209
  - lib/basketball/drafting/engine_serializer.rb
210
- - lib/basketball/drafting/entity.rb
211
210
  - lib/basketball/drafting/event.rb
212
211
  - lib/basketball/drafting/front_office.rb
213
212
  - lib/basketball/drafting/pick_event.rb
@@ -217,6 +216,8 @@ files:
217
216
  - lib/basketball/drafting/roster.rb
218
217
  - lib/basketball/drafting/sim_event.rb
219
218
  - lib/basketball/drafting/team.rb
219
+ - lib/basketball/entity.rb
220
+ - lib/basketball/value_object.rb
220
221
  - lib/basketball/version.rb
221
222
  homepage: https://github.com/mattruggio/basketball
222
223
  licenses:
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Basketball
4
- module Drafting
5
- class Entity
6
- extend Forwardable
7
- include Comparable
8
-
9
- attr_reader :id
10
-
11
- def_delegators :id, :to_s
12
-
13
- def initialize(id)
14
- raise ArgumentError, 'id is required' if id.to_s.empty?
15
-
16
- @id = id.to_s.upcase
17
- end
18
-
19
- def <=>(other)
20
- id <=> other.id
21
- end
22
-
23
- def ==(other)
24
- id == other.id
25
- end
26
- alias eql? ==
27
-
28
- def hash
29
- id.hash
30
- end
31
- end
32
- end
33
- end