basketball 0.0.8 → 0.0.10

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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +9 -19
  3. data/CHANGELOG.md +1 -39
  4. data/README.md +75 -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 +250 -0
  9. data/lib/basketball/app/coordinator_repository.rb +114 -0
  10. data/lib/basketball/app/document_repository.rb +67 -0
  11. data/lib/basketball/app/file_store.rb +38 -0
  12. data/lib/basketball/app/in_memory_store.rb +42 -0
  13. data/lib/basketball/app/league_repository.rb +20 -0
  14. data/lib/basketball/app/league_serializable.rb +54 -0
  15. data/lib/basketball/{draft/cli.rb → app/room_cli.rb} +74 -80
  16. data/lib/basketball/app/room_repository.rb +149 -0
  17. data/lib/basketball/app.rb +20 -0
  18. data/lib/basketball/draft/assessment.rb +31 -0
  19. data/lib/basketball/draft/event.rb +3 -2
  20. data/lib/basketball/draft/front_office.rb +35 -28
  21. data/lib/basketball/draft/{pick_event.rb → pick.rb} +13 -6
  22. data/lib/basketball/draft/room.rb +119 -119
  23. data/lib/basketball/draft/{player_search.rb → scout.rb} +4 -9
  24. data/lib/basketball/draft/skip.rb +12 -0
  25. data/lib/basketball/draft.rb +13 -6
  26. data/lib/basketball/entity.rb +19 -10
  27. data/lib/basketball/org/league.rb +68 -0
  28. data/lib/basketball/org/player.rb +26 -0
  29. data/lib/basketball/{draft → org}/position.rb +3 -2
  30. data/lib/basketball/org/team.rb +38 -0
  31. data/lib/basketball/org.rb +12 -0
  32. data/lib/basketball/season/arena.rb +113 -0
  33. data/lib/basketball/season/calendar.rb +41 -72
  34. data/lib/basketball/season/coordinator.rb +186 -128
  35. data/lib/basketball/season/{preseason_game.rb → exhibition.rb} +2 -1
  36. data/lib/basketball/season/game.rb +15 -10
  37. data/lib/basketball/season/matchup.rb +27 -0
  38. data/lib/basketball/season/opponent.rb +15 -0
  39. data/lib/basketball/season/{season_game.rb → regular.rb} +2 -1
  40. data/lib/basketball/season/result.rb +37 -0
  41. data/lib/basketball/season.rb +12 -13
  42. data/lib/basketball/value_object.rb +8 -27
  43. data/lib/basketball/value_object_dsl.rb +30 -0
  44. data/lib/basketball/version.rb +1 -1
  45. data/lib/basketball.rb +9 -4
  46. metadata +37 -44
  47. data/lib/basketball/draft/league.rb +0 -70
  48. data/lib/basketball/draft/player.rb +0 -43
  49. data/lib/basketball/draft/room_serializer.rb +0 -186
  50. data/lib/basketball/draft/roster.rb +0 -37
  51. data/lib/basketball/draft/sim_event.rb +0 -23
  52. data/lib/basketball/draft/skip_event.rb +0 -13
  53. data/lib/basketball/season/calendar_serializer.rb +0 -94
  54. data/lib/basketball/season/conference.rb +0 -57
  55. data/lib/basketball/season/division.rb +0 -43
  56. data/lib/basketball/season/league.rb +0 -114
  57. data/lib/basketball/season/league_serializer.rb +0 -99
  58. data/lib/basketball/season/scheduling_cli.rb +0 -198
  59. data/lib/basketball/season/team.rb +0 -21
@@ -2,30 +2,35 @@
2
2
 
3
3
  module Basketball
4
4
  module Season
5
+ # Base class describing what all games have in common.
5
6
  class Game < ValueObject
6
- attr_reader_value :date, :home_team, :away_team
7
+ value_reader :date, :home_opponent, :away_opponent
7
8
 
8
- def initialize(date:, home_team:, away_team:)
9
+ def initialize(date:, home_opponent:, away_opponent:)
9
10
  super()
10
11
 
11
12
  raise ArgumentError, 'date is required' unless date
12
- raise ArgumentError, 'home_team is required' unless home_team
13
- raise ArgumentError, 'away_team is required' unless away_team
14
- raise ArgumentError, 'teams cannot play themselves' if home_team == away_team
13
+ raise ArgumentError, 'home_opponent is required' unless home_opponent
14
+ raise ArgumentError, 'away_opponent is required' unless away_opponent
15
+ raise ArgumentError, 'teams cannot play themselves' if home_opponent == away_opponent
15
16
 
16
- @date = date
17
- @home_team = home_team
18
- @away_team = away_team
17
+ @date = date
18
+ @home_opponent = home_opponent
19
+ @away_opponent = away_opponent
19
20
 
20
21
  freeze
21
22
  end
22
23
 
24
+ def for?(team)
25
+ teams.include?(team)
26
+ end
27
+
23
28
  def teams
24
- [home_team, away_team]
29
+ [home_opponent, away_opponent]
25
30
  end
26
31
 
27
32
  def to_s
28
- "#{date} - #{away_team} at #{home_team}"
33
+ "#{date} - #{away_opponent} at #{home_opponent}"
29
34
  end
30
35
  end
31
36
  end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Basketball
4
+ module Season
5
+ # A Matchup is a late materialization of a game. While a game is a skeleton for a future
6
+ # matchup, it does not materialize until game time (rosters could change right up until game time).
7
+ class Matchup
8
+ class PlayersOnBothTeamsError < StandardError; end
9
+
10
+ attr_reader :game, :home_players, :away_players
11
+
12
+ def initialize(game:, home_players: [], away_players: [])
13
+ raise ArgumentError, 'game is required' unless game
14
+
15
+ @game = game
16
+ @home_players = home_players.uniq
17
+ @away_players = away_players.uniq
18
+
19
+ if home_players.intersect?(away_players)
20
+ raise PlayersOnBothTeamsError, 'players cannot be on both home and away team'
21
+ end
22
+
23
+ freeze
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Basketball
4
+ module Season
5
+ # Represents a team without a roster. Equal to a team by identity.
6
+ # A team's roster will not be known until the last minute (when it is game time).
7
+ class Opponent < Entity
8
+ def initialize(id:)
9
+ super(id)
10
+
11
+ freeze
12
+ end
13
+ end
14
+ end
15
+ end
@@ -2,7 +2,8 @@
2
2
 
3
3
  module Basketball
4
4
  module Season
5
- class SeasonGame < Game
5
+ # A regular season game.
6
+ class Regular < Game
6
7
  end
7
8
  end
8
9
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Basketball
4
+ module Season
5
+ # Base class describing the end result of a game. This should/could be sub-classed to include
6
+ # more sport-specific information.
7
+ class Result
8
+ extend Forwardable
9
+
10
+ class CannotTieError < StandardError; end
11
+
12
+ attr_reader :game, :home_score, :away_score
13
+
14
+ def_delegators :game, :date, :home_opponent, :away_opponent, :teams
15
+
16
+ def initialize(game:, home_score:, away_score:)
17
+ raise ArgumentError, 'game is required' unless game
18
+ raise CannotTieError, "#{game} ended in a tie" if home_score == away_score
19
+
20
+ @game = game
21
+ @home_score = home_score.to_i
22
+ @away_score = away_score.to_i
23
+ end
24
+
25
+ def to_s
26
+ "#{game.date} - #{away_opponent} (#{away_score}) at #{home_opponent} (#{home_score})"
27
+ end
28
+
29
+ def ==(other)
30
+ game == other.game &&
31
+ home_score == other.home_score &&
32
+ away_score == other.away_score
33
+ end
34
+ alias eql? ==
35
+ end
36
+ end
37
+ end
@@ -1,17 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'season/scheduling_cli'
3
+ # Common
4
+ require_relative 'season/arena'
5
+ require_relative 'season/calendar'
6
+ require_relative 'season/result'
7
+ require_relative 'season/game'
8
+ require_relative 'season/matchup'
9
+ require_relative 'season/opponent'
4
10
 
5
- module Basketball
6
- module Season
7
- class ConferenceAlreadyRegisteredError < StandardError; end
8
- class DivisionAlreadyRegisteredError < StandardError; end
9
- class TeamAlreadyRegisteredError < StandardError; end
11
+ # Game Subclasses
12
+ require_relative 'season/exhibition'
13
+ require_relative 'season/regular'
10
14
 
11
- class BadConferencesSizeError < StandardError; end
12
- class BadDivisionsSizeError < StandardError; end
13
- class BadTeamsSizeError < StandardError; end
14
-
15
- class UnknownTeamError < StandardError; end
16
- end
17
- end
15
+ # Specific
16
+ require_relative 'season/coordinator'
@@ -1,34 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'value_object_dsl'
4
+
3
5
  module Basketball
6
+ # A Value Object is something that has no specific identity, instead its identity is the sum of
7
+ # the attribute values. Changing one will change the entire object's identity.
8
+ # Comes with a very simple DSL provided by ValueObjectDSL for specifying properties along with
9
+ # base equality and sorting methods.
4
10
  class ValueObject
5
11
  include Comparable
6
-
7
- class << self
8
- def all_value_keys
9
- @all_value_keys ||= ancestors.flat_map do |ancestor|
10
- if ancestor < Basketball::ValueObject
11
- ancestor.value_keys
12
- else
13
- []
14
- end
15
- end.uniq.sort
16
- end
17
-
18
- def all_sorted_value_keys
19
- all_value_keys.sort
20
- end
21
-
22
- def value_keys
23
- @value_keys ||= []
24
- end
25
-
26
- def attr_reader_value(*keys)
27
- keys.each { |k| value_keys << k.to_sym }
28
-
29
- attr_reader(*keys)
30
- end
31
- end
12
+ extend ValueObjectDSL
32
13
 
33
14
  def to_s
34
15
  to_h.map { |k, v| "#{k}: #{v}" }.join(', ')
@@ -52,7 +33,7 @@ module Basketball
52
33
  alias eql? ==
53
34
 
54
35
  def hash
55
- all_sorted_values.map(&:hash).hash
36
+ all_sorted_values.hash
56
37
  end
57
38
 
58
39
  def all_sorted_values
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Basketball
4
+ # Class-level methods that extend core Ruby attr_* methods.
5
+ module ValueObjectDSL
6
+ def all_value_keys
7
+ @all_value_keys ||= ancestors.flat_map do |ancestor|
8
+ if ancestor < Basketball::ValueObject
9
+ ancestor.value_keys
10
+ else
11
+ []
12
+ end
13
+ end.uniq.sort
14
+ end
15
+
16
+ def all_sorted_value_keys
17
+ all_value_keys.sort
18
+ end
19
+
20
+ def value_keys
21
+ @value_keys ||= []
22
+ end
23
+
24
+ def value_reader(*keys)
25
+ keys.each { |k| value_keys << k.to_sym }
26
+
27
+ attr_reader(*keys)
28
+ end
29
+ end
30
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Basketball
4
- VERSION = '0.0.8'
4
+ VERSION = '0.0.10'
5
5
  end
data/lib/basketball.rb CHANGED
@@ -1,16 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'faker'
3
+ require 'date'
4
4
  require 'fileutils'
5
5
  require 'forwardable'
6
6
  require 'json'
7
- require 'securerandom'
8
7
  require 'slop'
9
8
 
10
- # Top-level
9
+ # Generic
11
10
  require_relative 'basketball/entity'
12
11
  require_relative 'basketball/value_object'
13
12
 
14
- # Submodules
13
+ # Dependent on Generic
14
+ require_relative 'basketball/org'
15
+
16
+ # Dependent on Org
15
17
  require_relative 'basketball/draft'
16
18
  require_relative 'basketball/season'
19
+
20
+ # Dependent on All
21
+ require_relative 'basketball/app'
metadata CHANGED
@@ -1,29 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: basketball
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.8
4
+ version: 0.0.10
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-16 00:00:00.000000000 Z
11
+ date: 2023-06-01 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: faker
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '3.2'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: '3.2'
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: slop
29
15
  requirement: !ruby/object:Gem::Requirement
@@ -38,15 +24,15 @@ dependencies:
38
24
  - - "~>"
39
25
  - !ruby/object:Gem::Version
40
26
  version: '4.10'
41
- description: |2
42
- This library is meant to serve as the domain for a basketball league/season simulator/turn-based game.
43
- It models core ideas such as: players, general managers, draft strategy, drafting, season generation, season simultation,
44
- playoff generation, playoff simulation, and more.
27
+ description: " This library is meant to serve as the domain for a basketball league/season
28
+ simulator/turn-based game. It models core ideas such as: players, general managers,
29
+ draft strategy, drafting, season generation, season simultation, playoff generation,
30
+ playoff simulation, and more.\n"
45
31
  email:
46
32
  - mattruggio@icloud.com
47
33
  executables:
48
- - basketball-draft
49
- - basketball-season-scheduling
34
+ - basketball-coordinator
35
+ - basketball-room
50
36
  extensions: []
51
37
  extra_rdoc_files: []
52
38
  files:
@@ -62,38 +48,45 @@ files:
62
48
  - README.md
63
49
  - Rakefile
64
50
  - basketball.gemspec
65
- - exe/basketball-draft
66
- - exe/basketball-season-scheduling
51
+ - exe/basketball-coordinator
52
+ - exe/basketball-room
67
53
  - lib/basketball.rb
54
+ - lib/basketball/app.rb
55
+ - lib/basketball/app/coordinator_cli.rb
56
+ - lib/basketball/app/coordinator_repository.rb
57
+ - lib/basketball/app/document_repository.rb
58
+ - lib/basketball/app/file_store.rb
59
+ - lib/basketball/app/in_memory_store.rb
60
+ - lib/basketball/app/league_repository.rb
61
+ - lib/basketball/app/league_serializable.rb
62
+ - lib/basketball/app/room_cli.rb
63
+ - lib/basketball/app/room_repository.rb
68
64
  - lib/basketball/draft.rb
69
- - lib/basketball/draft/cli.rb
65
+ - lib/basketball/draft/assessment.rb
70
66
  - lib/basketball/draft/event.rb
71
67
  - lib/basketball/draft/front_office.rb
72
- - lib/basketball/draft/league.rb
73
- - lib/basketball/draft/pick_event.rb
74
- - lib/basketball/draft/player.rb
75
- - lib/basketball/draft/player_search.rb
76
- - lib/basketball/draft/position.rb
68
+ - lib/basketball/draft/pick.rb
77
69
  - lib/basketball/draft/room.rb
78
- - lib/basketball/draft/room_serializer.rb
79
- - lib/basketball/draft/roster.rb
80
- - lib/basketball/draft/sim_event.rb
81
- - lib/basketball/draft/skip_event.rb
70
+ - lib/basketball/draft/scout.rb
71
+ - lib/basketball/draft/skip.rb
82
72
  - lib/basketball/entity.rb
73
+ - lib/basketball/org.rb
74
+ - lib/basketball/org/league.rb
75
+ - lib/basketball/org/player.rb
76
+ - lib/basketball/org/position.rb
77
+ - lib/basketball/org/team.rb
83
78
  - lib/basketball/season.rb
79
+ - lib/basketball/season/arena.rb
84
80
  - lib/basketball/season/calendar.rb
85
- - lib/basketball/season/calendar_serializer.rb
86
- - lib/basketball/season/conference.rb
87
81
  - lib/basketball/season/coordinator.rb
88
- - lib/basketball/season/division.rb
82
+ - lib/basketball/season/exhibition.rb
89
83
  - lib/basketball/season/game.rb
90
- - lib/basketball/season/league.rb
91
- - lib/basketball/season/league_serializer.rb
92
- - lib/basketball/season/preseason_game.rb
93
- - lib/basketball/season/scheduling_cli.rb
94
- - lib/basketball/season/season_game.rb
95
- - lib/basketball/season/team.rb
84
+ - lib/basketball/season/matchup.rb
85
+ - lib/basketball/season/opponent.rb
86
+ - lib/basketball/season/regular.rb
87
+ - lib/basketball/season/result.rb
96
88
  - lib/basketball/value_object.rb
89
+ - lib/basketball/value_object_dsl.rb
97
90
  - lib/basketball/version.rb
98
91
  homepage: https://github.com/mattruggio/basketball
99
92
  licenses:
@@ -123,5 +116,5 @@ requirements: []
123
116
  rubygems_version: 3.4.6
124
117
  signing_key:
125
118
  specification_version: 4
126
- summary: Basketball League Game Room
119
+ summary: Basketball Simulation Domain Model
127
120
  test_files: []
@@ -1,70 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'roster'
4
-
5
- module Basketball
6
- module Draft
7
- class League
8
- class RosterNotFoundError < StandardError; end
9
- class RosterAlreadyAddedError < StandardError; end
10
-
11
- attr_reader :free_agents, :rosters
12
-
13
- def initialize(free_agents: [], front_offices: [])
14
- @rosters = []
15
- @free_agents = []
16
-
17
- front_offices.each { |front_office| add_roster(front_office) }
18
- free_agents.each { |p| register!(player: p) }
19
-
20
- freeze
21
- end
22
-
23
- def roster(front_office)
24
- rosters.find { |r| r == front_office }
25
- end
26
-
27
- def register!(player:, front_office: nil)
28
- raise PlayerRequiredError, 'player is required' unless player
29
-
30
- rosters.each do |roster|
31
- if roster.registered?(player)
32
- raise PlayerAlreadyRegisteredError,
33
- "#{player} already registered to: #{roster.id}"
34
- end
35
- end
36
-
37
- if free_agents.include?(player)
38
- raise PlayerAlreadyRegisteredError,
39
- "#{player} already registered as a free agent"
40
- end
41
-
42
- if front_office
43
- roster = roster(front_office)
44
-
45
- raise RosterNotFoundError, "Roster not found for: #{front_office}" unless roster
46
-
47
- roster.sign!(player)
48
- else
49
- free_agents << player
50
- end
51
-
52
- self
53
- end
54
-
55
- def to_s
56
- (['League'] + rosters.map(&:to_s)).join("\n")
57
- end
58
-
59
- private
60
-
61
- def add_roster(front_office)
62
- raise RosterAlreadyAddedError, "#{front_office} already added" if rosters.include?(front_office)
63
-
64
- rosters << Roster.new(id: front_office.id, name: front_office.name)
65
-
66
- self
67
- end
68
- end
69
- end
70
- end
@@ -1,43 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Basketball
4
- module Draft
5
- class Player < Entity
6
- STAR_THRESHOLD = 75
7
- OVERALL_STAR_INDICATOR = '⭐'
8
-
9
- private_constant :STAR_THRESHOLD, :OVERALL_STAR_INDICATOR
10
-
11
- attr_reader :first_name, :last_name, :position, :overall
12
-
13
- def initialize(id:, position:, first_name: '', last_name: '', overall: 0)
14
- super(id)
15
-
16
- raise ArgumentError, 'position is required' unless position
17
-
18
- @first_name = first_name.to_s
19
- @last_name = last_name.to_s
20
- @position = position
21
- @overall = overall.to_i
22
-
23
- freeze
24
- end
25
-
26
- def full_name
27
- "#{first_name.strip} #{last_name.strip}".strip
28
- end
29
-
30
- def to_s
31
- "[#{super}] #{full_name} (#{position}) #{overall} #{star_indicators.join(', ')}".strip
32
- end
33
-
34
- private
35
-
36
- def star_indicators
37
- [].tap do |indicators|
38
- indicators << OVERALL_STAR_INDICATOR if overall >= STAR_THRESHOLD
39
- end
40
- end
41
- end
42
- end
43
- end