basketball 0.0.25 → 0.0.27

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: e02bf31c8332d0d1010f8ad9ff18cb09ddd3f97e4a7cefcb14ddf11d38633d67
4
- data.tar.gz: 9c4b3b0e1771c0a4d22e8c83ba9040089fae019f3726c8cda55ddecbb3d69fa1
3
+ metadata.gz: 3fdbeeaeb0a6e0382cc1af0031199691ac64ba039b3bfa4fa6bde0ac0bde2b17
4
+ data.tar.gz: 3ed2a71522e8aae3aa78cd5af06941f966341b01f4c85a81a04f772b561a349d
5
5
  SHA512:
6
- metadata.gz: 65a444b460b134eeceecea00213a0e4887dc635b7d012f402e09404e4d3e2dea44931810f413da167ae72ea6fb9e2b1533aea4691d3b5d99ba841709388f95e0
7
- data.tar.gz: b193b20e0ba49d4cf4686150cc7abef344a3289bcefdeb4bfc168e57e2eb8f1c6d776e3eafdc6a618096ea29e0ae9d3e94d96b3c78f341efc461efab6f746b16
6
+ metadata.gz: 2b76fa1d6b90f0c93e49b73c44028792909d94da7a42a472c923e9c23ab504fdd3acfcf037696479fa2851e9cd106c83dca4b951ead2dc25b02e9d5d996eee2c
7
+ data.tar.gz: 6ef6f13785dc387ad3df3feeb55cd9d6f2619fa992cd866b38819d4e4c249862b7400c65c0a44552c7e483750068e7957e9776d35f37fbe05e993a08f4574802
data/.rubocop.yml CHANGED
@@ -19,7 +19,7 @@ RSpec/NestedGroups:
19
19
  Max: 5
20
20
 
21
21
  RSpec/MultipleExpectations:
22
- Max: 2
22
+ Max: 10
23
23
 
24
24
  Metrics/ParameterLists:
25
25
  Max: 6
@@ -7,12 +7,14 @@ module Basketball
7
7
  value_reader :date,
8
8
  :home,
9
9
  :opponent_score,
10
+ :opponent_type,
10
11
  :opponent,
11
12
  :score
12
13
 
13
14
  alias home? home
14
15
 
15
- def initialize(date:, home:, opponent:, opponent_score:, score:)
16
+ # rubocop:disable Metrics/CyclomaticComplexity
17
+ def initialize(date:, home:, opponent:, opponent_score:, score:, opponent_type:)
16
18
  super()
17
19
 
18
20
  raise ArgumentError, 'date is required' unless date
@@ -21,15 +23,30 @@ module Basketball
21
23
  raise ArgumentError, 'opponent_score is required' unless opponent_score
22
24
  raise ArgumentError, 'home is required' if home.nil?
23
25
  raise CannotTieError, 'scores cannot be equal' if score == opponent_score
26
+ raise ArgumentError, 'opponent_type is required' unless opponent_type
24
27
 
25
28
  @date = date
26
29
  @opponent = opponent
27
30
  @score = score
28
31
  @opponent_score = opponent_score
29
32
  @home = home
33
+ @opponent_type = OpponentType.parse(opponent_type)
30
34
 
31
35
  freeze
32
36
  end
37
+ # rubocop:enable Metrics/CyclomaticComplexity
38
+
39
+ def divisional?
40
+ opponent_type == INTRA_DIVISIONAL
41
+ end
42
+
43
+ def intra_conference?
44
+ opponent_type == INTRA_CONFERENCE
45
+ end
46
+
47
+ def inter_conterence?
48
+ opponent_type == INTER_CONFERENCE
49
+ end
33
50
 
34
51
  def win?
35
52
  score > opponent_score
@@ -40,7 +57,10 @@ module Basketball
40
57
  end
41
58
 
42
59
  def to_s
43
- "[#{date}] #{win? ? 'Win' : 'Loss'} #{home? ? 'vs' : 'at'} #{opponent} (#{score}-#{opponent_score})"
60
+ win_display = win? ? 'win' : 'loss'
61
+ vs_display = home? ? 'vs' : 'at'
62
+
63
+ "[#{date}] #{opponent_type} #{win_display} #{vs_display} #{opponent} (#{score}-#{opponent_score})"
44
64
  end
45
65
  end
46
66
  end
@@ -4,19 +4,21 @@ module Basketball
4
4
  module Season
5
5
  # Base class describing what all games have in common.
6
6
  class Game < ValueObject
7
- value_reader :date, :home_opponent, :away_opponent
7
+ value_reader :date, :home_opponent, :away_opponent, :opponent_type
8
8
 
9
- def initialize(date:, home_opponent:, away_opponent:)
9
+ def initialize(date:, home_opponent:, away_opponent:, opponent_type:)
10
10
  super()
11
11
 
12
12
  raise ArgumentError, 'date is required' unless date
13
13
  raise ArgumentError, 'home_opponent is required' unless home_opponent
14
14
  raise ArgumentError, 'away_opponent is required' unless away_opponent
15
15
  raise ArgumentError, 'teams cannot play themselves' if home_opponent == away_opponent
16
+ raise ArgumentError, 'opponent_type is required' unless opponent_type
16
17
 
17
18
  @date = date
18
19
  @home_opponent = home_opponent
19
20
  @away_opponent = away_opponent
21
+ @opponent_type = OpponentType.parse(opponent_type)
20
22
 
21
23
  freeze
22
24
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Basketball
4
+ module Season
5
+ # Describes the relationship between two teams.
6
+ module OpponentType
7
+ INTRA_DIVISIONAL = :intra_divisional
8
+ INTRA_CONFERENCE = :intra_conference
9
+ INTER_CONFERENCE = :inter_conference
10
+
11
+ class << self
12
+ def parse(value)
13
+ OpponentType.const_get(value.to_s.upcase.to_sym)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -19,29 +19,9 @@ module Basketball
19
19
  end
20
20
 
21
21
  def accept!(result)
22
- if result.home_opponent == self
23
- detail = Detail.new(
24
- date: result.date,
25
- opponent: result.away_opponent,
26
- score: result.home_score,
27
- opponent_score: result.away_score,
28
- home: true
29
- )
30
-
31
- add!(detail)
32
- elsif result.away_opponent == self
33
- detail = Detail.new(
34
- date: result.date,
35
- opponent: result.home_opponent,
36
- score: result.away_score,
37
- opponent_score: result.home_score,
38
- home: false
39
- )
40
-
41
- add!(detail)
42
- else
43
- raise OpponentNotFoundError, "#{result} has no opponent for #{self}"
44
- end
22
+ detail = result_to_detail(result)
23
+
24
+ add!(detail)
45
25
  end
46
26
 
47
27
  def detail_for(date)
@@ -52,26 +32,28 @@ module Basketball
52
32
  details_by_date.values
53
33
  end
54
34
 
55
- def win_percentage
35
+ def win_percentage(opponent_type = nil)
36
+ game_count = game_count(opponent_type)
37
+
56
38
  return 0 unless game_count.positive?
57
39
 
58
- (win_count.to_f / game_count).round(3)
40
+ (win_count(opponent_type).to_f / game_count).round(3)
59
41
  end
60
42
 
61
- def win_percentage_display
62
- format('%.3f', win_percentage)
43
+ def win_percentage_display(opponent_type = nil)
44
+ format('%.3f', win_percentage(opponent_type))
63
45
  end
64
46
 
65
- def game_count
66
- details.length
47
+ def game_count(opponent_type)
48
+ details_for(opponent_type).length
67
49
  end
68
50
 
69
- def win_count
70
- details.count(&:win?)
51
+ def win_count(opponent_type = nil)
52
+ details_for(opponent_type).count(&:win?)
71
53
  end
72
54
 
73
- def loss_count
74
- details.count(&:loss?)
55
+ def loss_count(opponent_type = nil)
56
+ details_for(opponent_type).count(&:loss?)
75
57
  end
76
58
 
77
59
  def to_s
@@ -93,6 +75,38 @@ module Basketball
93
75
  private
94
76
 
95
77
  attr_reader :details_by_date
78
+
79
+ def details_for(opponent_type = nil)
80
+ details.select { |d| opponent_type.nil? || d.opponent_type == opponent_type }
81
+ end
82
+
83
+ def result_to_detail(result)
84
+ base_args = {
85
+ date: result.date,
86
+ opponent_type: result.opponent_type
87
+ }
88
+
89
+ all_args =
90
+ if result.home_opponent == self
91
+ base_args.merge(
92
+ opponent: result.away_opponent,
93
+ score: result.home_score,
94
+ opponent_score: result.away_score,
95
+ home: true
96
+ )
97
+ elsif result.away_opponent == self
98
+ base_args.merge(
99
+ opponent: result.home_opponent,
100
+ score: result.away_score,
101
+ opponent_score: result.home_score,
102
+ home: false
103
+ )
104
+ else
105
+ raise OpponentNotFoundError, "#{result} has no opponent for #{self}"
106
+ end
107
+
108
+ Detail.new(**all_args)
109
+ end
96
110
  end
97
111
  end
98
112
  end
@@ -11,7 +11,7 @@ module Basketball
11
11
 
12
12
  attr_reader :game, :home_score, :away_score
13
13
 
14
- def_delegators :game, :date, :home_opponent, :away_opponent, :teams
14
+ def_delegators :game, :date, :home_opponent, :away_opponent, :teams, :opponent_type
15
15
 
16
16
  def initialize(game:, home_score:, away_score:)
17
17
  raise ArgumentError, 'game is required' unless game
@@ -160,16 +160,19 @@ module Basketball
160
160
  matchups = matchup_plan(league)
161
161
 
162
162
  matchups.each do |(team1, team2), count|
163
- candidates = calendar.available_regular_matchup_dates(team1, team2)
164
- dates = candidates.sample(count)
165
- games = balanced_games(dates, team1, team2)
163
+ determine_opponent_type(league, team1, team2)
164
+ candidates = calendar.available_regular_matchup_dates(team1, team2)
165
+ dates = candidates.sample(count)
166
+ games = balanced_games(dates, team1, team2, league)
166
167
 
167
168
  games.each { |game| calendar.add!(game) }
168
169
  end
169
170
  end
170
171
 
171
- def balanced_games(dates, team1, team2)
172
+ def balanced_games(dates, team1, team2, league)
172
173
  dates.map.with_index(1) do |date, index|
174
+ opponent_type = determine_opponent_type(league, team1, team2)
175
+
173
176
  home_opponent, away_opponent =
174
177
  if index.even?
175
178
  [Opponent.from(team1), Opponent.from(team2)]
@@ -177,7 +180,7 @@ module Basketball
177
180
  [Opponent.from(team2), Opponent.from(team1)]
178
181
  end
179
182
 
180
- Regular.new(date:, home_opponent:, away_opponent:)
183
+ Regular.new(date:, home_opponent:, away_opponent:, opponent_type:)
181
184
  end
182
185
  end
183
186
 
@@ -198,8 +201,9 @@ module Basketball
198
201
 
199
202
  next if candidates.empty?
200
203
 
201
- date = candidates.sample
202
- game = random_exhibition_game(date, team, other_team)
204
+ opponent_type = determine_opponent_type(league, team, other_team)
205
+ date = candidates.sample
206
+ game = random_exhibition_game(date, team, other_team, opponent_type)
203
207
 
204
208
  calendar.add!(game)
205
209
 
@@ -208,7 +212,17 @@ module Basketball
208
212
  end
209
213
  end
210
214
 
211
- def random_exhibition_game(date, team1, team2)
215
+ def determine_opponent_type(league, team1, team2)
216
+ if league.division_for(team1) == league.division_for(team2)
217
+ OpponentType::INTRA_DIVISIONAL
218
+ elsif league.conference_for(team1) == league.conference_for(team2)
219
+ OpponentType::INTRA_CONFERENCE
220
+ else
221
+ OpponentType::INTER_CONFERENCE
222
+ end
223
+ end
224
+
225
+ def random_exhibition_game(date, team1, team2, opponent_type)
212
226
  home_opponent, away_opponent =
213
227
  if rand(1..2) == 1
214
228
  [Opponent.from(team1), Opponent.from(team2)]
@@ -216,7 +230,7 @@ module Basketball
216
230
  [Opponent.from(team2), Opponent.from(team1)]
217
231
  end
218
232
 
219
- Exhibition.new(date:, home_opponent:, away_opponent:)
233
+ Exhibition.new(date:, home_opponent:, away_opponent:, opponent_type:)
220
234
  end
221
235
  end
222
236
  end
@@ -6,6 +6,7 @@ require_relative 'season/calendar'
6
6
  require_relative 'season/game'
7
7
  require_relative 'season/matchup'
8
8
  require_relative 'season/opponent'
9
+ require_relative 'season/opponent_type'
9
10
  require_relative 'season/result'
10
11
 
11
12
  # League Common
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Basketball
4
- VERSION = '0.0.25'
4
+ VERSION = '0.0.27'
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.25
4
+ version: 0.0.27
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-06-30 00:00:00.000000000 Z
11
+ date: 2023-07-25 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: " This library is meant to serve as the domain for a basketball league/season
14
14
  simulator/turn-based game. It models core ideas such as: players, general managers,
@@ -62,6 +62,7 @@ files:
62
62
  - lib/basketball/season/league.rb
63
63
  - lib/basketball/season/matchup.rb
64
64
  - lib/basketball/season/opponent.rb
65
+ - lib/basketball/season/opponent_type.rb
65
66
  - lib/basketball/season/player.rb
66
67
  - lib/basketball/season/position.rb
67
68
  - lib/basketball/season/record.rb