basketball 0.0.14 → 0.0.15

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: f115c20b1dfbaf07e291b7419481f55e089f416963b6edfb6a8d7ceab3d760d1
4
- data.tar.gz: 821d8d53808e33a2ee8e47e022e4d6ff041dbd385350563e87ed365595419df5
3
+ metadata.gz: 27530180da34588494d04824d1fe1e9a6bfd4e0d94adc98afde3f659b070759c
4
+ data.tar.gz: 4f8e961e2e8ac1bedd5d0869d5f5f8e0e2498cb24465ef0d4505f20d4219b7fc
5
5
  SHA512:
6
- metadata.gz: cd80e53d23a2379e009c2a554ddc99a807cd0e7e4069b4e69123ca1fccba14bf96d2b422d5f9d347ccd9be90c1a5b5e6d59b9235603f4fe5b9ab4f25ea218846
7
- data.tar.gz: ad921f64b359ac4466c5a16cc69c3e989f75ebd0a2a477d81fd3dd5df6db8d8de713be2b56f281b912e5bc02a1b97e5e928b4e0c3cd53da27cb62b8999601282
6
+ metadata.gz: bbb24ef7d1f192e39dd3e0740d60210e47fcdc041e6b69ec8d9a2897a39514dca775439c8c2894f228afc6ef8ebedf2005a83ea7b8817d00247a69e5e0ae2177
7
+ data.tar.gz: 3c24e207c24e33c49baadb8bf13f8107a2bea9dc1d8bf6404601551635ef9a375f03f4b27da0b18118ef37eea55cda7d7e24b2572fd1649bc012d3a7081c9928
data/README.md CHANGED
@@ -12,11 +12,8 @@ Element | Description
12
12
  :------------ | :-----------
13
13
  **Arena** | Determines exhibition and regular season game outcomes.
14
14
  **Assessment** | When the Room needs to know who a Front Office wants to select, the Room will send the Front Office an Assessment. The Assessment is a report of where the team currently stands: players picked, players available, and round information.
15
- **basketball-draft-room** | Command-line executable script showing an example of how to consume the draft room aggregate root.
16
- **basketball-season-coordinator** | Command-line executable script showing an example of how to consume the season coordinator aggregate root.
17
15
  **Calendar** | Stores important boundary dates (exhibition start, exhibition end, season start, and season end).
18
16
  **Conference** | A collection of Divisions.
19
- **Coordinator CLI** | Underlying Ruby class that powers the `basketball-season-coordinator` script. Basically a terminal wrapper over the Coordinator object.
20
17
  **Coordinator Repository** | Understands how to save and load Coordinator objects from JSON files on disk.
21
18
  **Coordinator** | Object which can take a League, Calendar, Games, and an Arena and provide an iterable interface to enumerate through days and simulate games as results.
22
19
  **Detail** | Re-representation of a Result object but from a specific team's perspective.
@@ -25,7 +22,7 @@ Element | Description
25
22
  **Exhibition** | Pre-season game which has no impact to team record.
26
23
  **External Dependency** | Some outside system which this library or portions of this library are dependent on.
27
24
  **File Store** | Implements a store that can interact with the underlying File System.
28
- **File System** | Local operating system that the CLI will use for persistence.
25
+ **File System** | Local operating system that repositories can use as their underlying persistence layer.
29
26
  **Front Office** | Identifiable as a team, contains logic for how to auto-pick draft selections. Meant to be subclassed and extended to include more intricate player selection logic as the base will simply randomly select a player.
30
27
  **Game** | Matches up a date with two teams (home and away) to represent a coordinatord match-up.
31
28
  **League Repository** | Understands how to save and load League objects from JSON files on disk.
@@ -37,7 +34,6 @@ Element | Description
37
34
  **Record** | Represents a team's overall record.
38
35
  **Regular** | Game that counts towards regular season record.
39
36
  **Result** | The outcome of a game (typically with a home and away score).
40
- **Room CLI** | Underlying Ruby class that powers the `basketball-draft-room` script. Basically a terminal wrapper for the Room object.
41
37
  **Room Repository** | Understands how to save and load Room objects from JSON files on disk.
42
38
  **Room** | Main object responsible for providing an iterable interface capable of executing a draft, pick by pick.
43
39
  **Scheduler** | Knows how to take a League and a year and generate a game-populated calendar.
@@ -72,70 +68,6 @@ The input for the main object `Basketball::Draft::Room` is an array of teams (`B
72
68
  * **Basketball::Draft::Room#pick!(player)**: Pick an exact player for the current front office.
73
69
  * **Basketball::Draft::Room#sim_rest!**: Simulate the rest of the picks.
74
70
 
75
- #### Command Line Interface
76
-
77
- This library ships with an example of how the Draft module could be used implemented as a command-line executable script. The script is file-based and will de-serialize into a Room object, execute operations, then serialize and write it back to disk.
78
-
79
- ###### Generate a Fresh Draft
80
-
81
- ```zsh
82
- basketball-draft-room -o tmp/draft.json
83
- ```
84
-
85
- ###### N Top Available Players
86
-
87
- ```zsh
88
- basketball-draft-room -i tmp/draft.json -t 10
89
- ```
90
-
91
- ###### N Top Available Players for a Position
92
-
93
- ```zsh
94
- basketball-draft-room -i tmp/draft.json -t 10 -q PG
95
- ```
96
-
97
- ###### Output League
98
-
99
- ```zsh
100
- basketball-draft-room -i tmp/draft.json -l
101
- ```
102
-
103
- ###### Output Event Log
104
-
105
- ```zsh
106
- basketball-draft-room -i tmp/draft.json -e
107
- ```
108
-
109
- ###### Simulate N Picks
110
-
111
- ```zsh
112
- basketball-draft-room -i tmp/draft.json -s 10
113
- ```
114
-
115
- ###### Skip N Picks
116
-
117
- ```zsh
118
- basketball-draft-room -i tmp/draft.json -x 10
119
- ```
120
-
121
- ###### Pick Players
122
-
123
- ```zsh
124
- basketball-draft-room -i tmp/draft.json -p P-100,P-200,P-300
125
- ```
126
-
127
- ###### Simulate the Rest of the Draft
128
-
129
- ```zsh
130
- basketball-draft-room -i tmp/draft.json -a
131
- ```
132
-
133
- ###### Help Menu
134
-
135
- ```zsh
136
- basketball-draft-room -h
137
- ```
138
-
139
71
  ### Season Module
140
72
 
141
73
  The Season module knows how to execute a calendar of games for a League and generate results. The main object is the `Basketball::Season::Coordinator` class. Once instantiated there are four main methods:
@@ -143,46 +75,6 @@ The Season module knows how to execute a calendar of games for a League and gene
143
75
  * **Basketball::Season::Coordinator#sim!**: Simulate the next day of games.
144
76
  * **Basketball::Season::Coordinator#sim_rest!**: Simulate the rest of the games.
145
77
 
146
- #### Command Line Interface
147
-
148
- This library ships with an example of how the Season module could be used implemented as a command-line executable script. The script is file-based and will de-serialize into a Coordinator object, execute operations, then serialize and write it back to disk.
149
-
150
- ###### Generate Random coordinator
151
-
152
- ```zsh
153
- exe/basketball-season-coordinator -o tmp/coordinator.json
154
- ```
155
-
156
- ###### Sim One Day and Save to New File
157
-
158
- ```zsh
159
- exe/basketball-season-coordinator -i tmp/coordinator.json -o tmp/coordinator2.json -d 1
160
- ```
161
-
162
- ###### Output Event Log
163
-
164
- ```zsh
165
- exe/basketball-season-coordinator -i tmp/coordinator.json -e
166
- ```
167
-
168
- ###### Sim Two Days and Save To Input File
169
-
170
- ```zsh
171
- exe/basketball-season-coordinator -i tmp/coordinator.json -d 2
172
- ```
173
-
174
- ###### Sim Rest of Calendar
175
-
176
- ```zsh
177
- exe/basketball-season-coordinator -i tmp/coordinator.json -a
178
- ```
179
-
180
- ###### Help Menu
181
-
182
- ```zsh
183
- basketball-season-coordinator -h
184
- ```
185
-
186
78
  ## Contributing
187
79
 
188
80
  ### Development Environment Configuration
data/basketball.gemspec CHANGED
@@ -15,13 +15,7 @@ Gem::Specification.new do |s|
15
15
  s.email = ['mattruggio@icloud.com']
16
16
  s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(.github|bin|docs|spec)/}) }
17
17
  s.bindir = 'exe'
18
-
19
- s.executables = %w[
20
- basketball
21
- basketball-season-coordinator
22
- basketball-draft-room
23
- ]
24
-
18
+ s.executables = %w[]
25
19
  s.homepage = 'https://github.com/mattruggio/basketball'
26
20
  s.license = 'MIT'
27
21
  s.metadata = {
@@ -34,6 +28,4 @@ Gem::Specification.new do |s|
34
28
  }
35
29
 
36
30
  s.required_ruby_version = '>= 3.2.1'
37
-
38
- s.add_dependency('slop', '~>4.10')
39
31
  end
@@ -4,8 +4,6 @@ module Basketball
4
4
  module App
5
5
  # Knows how to flatten a Coordinator instance and rehydrate one from JSON and/or a Ruby hash.
6
6
  class CoordinatorRepository < DocumentRepository
7
- include LeagueSerializable
8
-
9
7
  GAME_CLASSES = {
10
8
  'Exhibition' => Season::Exhibition,
11
9
  'Regular' => Season::Regular
@@ -19,8 +17,7 @@ module Basketball
19
17
  Season::Coordinator.new(
20
18
  calendar: deserialize_calendar(hash[:calendar]),
21
19
  current_date: Date.parse(hash[:current_date]),
22
- results: deserialize_results(hash[:results]),
23
- league: deserialize_league(hash[:league])
20
+ results: deserialize_results(hash[:results])
24
21
  )
25
22
  end
26
23
 
@@ -29,8 +26,7 @@ module Basketball
29
26
  id: coordinator.id,
30
27
  calendar: serialize_calendar(coordinator.calendar),
31
28
  current_date: coordinator.current_date.to_s,
32
- results: serialize_results(coordinator.results),
33
- league: serialize_league(coordinator.league)
29
+ results: serialize_results(coordinator.results)
34
30
  }
35
31
  end
36
32
 
@@ -4,8 +4,6 @@ module Basketball
4
4
  module App
5
5
  # Knows how to flatten a League instance and rehydrate one from JSON and/or a Ruby hash.
6
6
  class LeagueRepository < DocumentRepository
7
- include LeagueSerializable
8
-
9
7
  private
10
8
 
11
9
  def from_h(hash)
@@ -15,6 +13,97 @@ module Basketball
15
13
  def to_h(league)
16
14
  serialize_league(league)
17
15
  end
16
+
17
+ # Serialization
18
+
19
+ def serialize_league(league)
20
+ {
21
+ id: league.id,
22
+ conferences: serialize_conferences(league.conferences)
23
+ }
24
+ end
25
+
26
+ def serialize_conferences(conferences)
27
+ conferences.map do |conference|
28
+ {
29
+ id: conference.id,
30
+ divisions: serialize_divisions(conference.divisions)
31
+ }
32
+ end
33
+ end
34
+
35
+ def serialize_divisions(divisions)
36
+ divisions.map do |division|
37
+ {
38
+ id: division.id,
39
+ teams: serialize_teams(division.teams)
40
+ }
41
+ end
42
+ end
43
+
44
+ def serialize_teams(teams)
45
+ teams.map do |team|
46
+ {
47
+ id: team.id,
48
+ players: serialize_players(team.players)
49
+ }
50
+ end
51
+ end
52
+
53
+ def serialize_players(players)
54
+ players.map do |player|
55
+ {
56
+ id: player.id,
57
+ overall: player.overall,
58
+ position: player.position&.code
59
+ }
60
+ end
61
+ end
62
+
63
+ # Deserialization
64
+
65
+ def deserialize_league(league_hash)
66
+ Org::League.new(
67
+ conferences: deserialize_conferences(league_hash[:conferences])
68
+ )
69
+ end
70
+
71
+ def deserialize_conferences(conference_hashes)
72
+ (conference_hashes || []).map do |conference_hash|
73
+ Org::Conference.new(
74
+ id: conference_hash[:id],
75
+ divisions: deserialize_divisions(conference_hash[:divisions])
76
+ )
77
+ end
78
+ end
79
+
80
+ def deserialize_divisions(division_hashes)
81
+ (division_hashes || []).map do |division_hash|
82
+ Org::Division.new(
83
+ id: division_hash[:id],
84
+ teams: deserialize_teams(division_hash[:teams])
85
+ )
86
+ end
87
+ end
88
+
89
+ def deserialize_teams(team_hashes)
90
+ (team_hashes || []).map do |team_hash|
91
+ Org::Team.new(
92
+ id: team_hash[:id],
93
+ players: deserialize_players(team_hash[:players])
94
+ )
95
+ end
96
+ end
97
+
98
+ def deserialize_players(player_hashes)
99
+ (player_hashes || []).map do |player_hash|
100
+ Org::Player.new(
101
+ id: player_hash[:id],
102
+ overall: player_hash[:overall],
103
+ position: Org::Position.new(player_hash[:position])
104
+ )
105
+ end
106
+ end
18
107
  end
19
108
  end
20
109
  end
@@ -26,6 +26,7 @@ module Basketball
26
26
 
27
27
  def to_h(room)
28
28
  {
29
+ id: room.id,
29
30
  rounds: room.rounds,
30
31
  front_offices: room.front_offices.map { |fo| serialize_front_office(fo) },
31
32
  players: room.players.map { |p| serialize_player(p) },
@@ -4,9 +4,6 @@
4
4
  require_relative 'app/file_store'
5
5
  require_relative 'app/in_memory_store'
6
6
 
7
- # Serialization
8
- require_relative 'app/league_serializable'
9
-
10
7
  # Repositories / Common
11
8
  require_relative 'app/document_repository'
12
9
 
@@ -14,7 +11,3 @@ require_relative 'app/document_repository'
14
11
  require_relative 'app/coordinator_repository'
15
12
  require_relative 'app/league_repository'
16
13
  require_relative 'app/room_repository'
17
-
18
- # Controllers
19
- require_relative 'app/coordinator_cli'
20
- require_relative 'app/room_cli'
@@ -90,8 +90,6 @@ module Basketball
90
90
  other_divisions.flat_map(&:teams)
91
91
  end
92
92
 
93
- private
94
-
95
93
  def team_for(id)
96
94
  teams.find { |team| team.id == id }
97
95
  end
@@ -11,14 +11,12 @@ module Basketball
11
11
  class OutOfBoundsError < StandardError; end
12
12
  class PlayedGamesError < StandardError; end
13
13
  class UnknownGameError < StandardError; end
14
- class UnknownTeamError < StandardError; end
15
14
  class UnplayedGamesError < StandardError; end
16
15
 
17
16
  attr_reader :calendar,
18
17
  :current_date,
19
18
  :arena,
20
- :results,
21
- :league
19
+ :results
22
20
 
23
21
  def_delegators :calendar,
24
22
  :exhibition_start_date,
@@ -30,37 +28,29 @@ module Basketball
30
28
  :regulars_for,
31
29
  :games_for
32
30
 
33
- def initialize(
34
- calendar:,
35
- current_date:,
36
- results: [],
37
- league: Org::League.new
38
- )
31
+ def initialize(calendar:, current_date:, results: [])
39
32
  super()
40
33
 
41
34
  raise ArgumentError, 'calendar is required' unless calendar
42
35
  raise ArgumentError, 'current_date is required' if current_date.to_s.empty?
43
- raise ArgumentError, 'league is required' unless league
44
36
 
45
37
  @calendar = calendar
46
38
  @current_date = current_date
47
39
  @arena = Arena.new
48
40
  @results = []
49
- @league = league
50
41
 
51
42
  results.each { |result| replay!(result) }
52
43
 
53
44
  assert_current_date
54
45
  assert_all_past_dates_are_played
55
46
  assert_all_future_dates_arent_played
56
- assert_all_known_teams
57
47
  end
58
48
 
59
- def sim_rest!(&)
49
+ def sim_rest!(league, &)
60
50
  events = []
61
51
 
62
52
  while not_done?
63
- new_events = sim!(&)
53
+ new_events = sim!(league, &)
64
54
 
65
55
  events += new_events
66
56
  end
@@ -78,15 +68,17 @@ module Basketball
78
68
  raise OutOfBoundsError, "current date #{current_date} should be on or after #{exhibition_start_date}"
79
69
  end
80
70
 
81
- def sim!
71
+ def sim!(league)
72
+ raise ArgumentError, 'league is required' unless league
73
+
82
74
  return [] if done?
83
75
 
84
76
  events = []
85
77
  games = games_for(date: current_date)
86
78
 
87
79
  games.each do |game|
88
- home_players = opponent_team(game.home_opponent).players
89
- away_players = opponent_team(game.away_opponent).players
80
+ home_players = opponent_team(league, game.home_opponent).players
81
+ away_players = opponent_team(league, game.away_opponent).players
90
82
  matchup = Matchup.new(game:, home_players:, away_players:)
91
83
  event = arena.play(matchup)
92
84
 
@@ -140,7 +132,6 @@ module Basketball
140
132
 
141
133
  def add!(game)
142
134
  assert_today_or_in_future(game)
143
- assert_known_teams(game)
144
135
 
145
136
  calendar.add!(game)
146
137
 
@@ -165,7 +156,7 @@ module Basketball
165
156
 
166
157
  attr_writer :arena
167
158
 
168
- def opponent_team(opponent)
159
+ def opponent_team(league, opponent)
169
160
  league.teams.find { |t| t == opponent }
170
161
  end
171
162
 
@@ -191,14 +182,6 @@ module Basketball
191
182
  raise OutOfBoundsError, "#{game.date} is on or before the current date (#{current_date})"
192
183
  end
193
184
 
194
- def assert_known_teams(game)
195
- raise UnknownTeamError, "unknown opponent: #{game.home_opponent}" unless league.team?(game.home_opponent)
196
-
197
- return if league.team?(game.away_opponent)
198
-
199
- raise UnknownTeamError, "unknown opponent: #{game.away_opponent}"
200
- end
201
-
202
185
  def assert_all_past_dates_are_played
203
186
  games_that_should_be_played = games.select { |game| game.date < current_date }
204
187
 
@@ -237,10 +220,6 @@ module Basketball
237
220
 
238
221
  result
239
222
  end
240
-
241
- def assert_all_known_teams
242
- calendar.games.each { |game| assert_known_teams(game) }
243
- end
244
223
  end
245
224
  end
246
225
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Basketball
4
- VERSION = '0.0.14'
4
+ VERSION = '0.0.15'
5
5
  end
data/lib/basketball.rb CHANGED
@@ -4,7 +4,6 @@ require 'date'
4
4
  require 'fileutils'
5
5
  require 'forwardable'
6
6
  require 'json'
7
- require 'slop'
8
7
 
9
8
  # Generic
10
9
  require_relative 'basketball/entity'
metadata CHANGED
@@ -1,39 +1,22 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: basketball
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.14
4
+ version: 0.0.15
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-09 00:00:00.000000000 Z
12
- dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: slop
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '4.10'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: '4.10'
11
+ date: 2023-06-12 00:00:00.000000000 Z
12
+ dependencies: []
27
13
  description: " This library is meant to serve as the domain for a basketball league/season
28
14
  simulator/turn-based game. It models core ideas such as: players, general managers,
29
15
  draft strategy, drafting, season generation, season simultation, playoff generation,
30
16
  playoff simulation, and more.\n"
31
17
  email:
32
18
  - mattruggio@icloud.com
33
- executables:
34
- - basketball
35
- - basketball-season-coordinator
36
- - basketball-draft-room
19
+ executables: []
37
20
  extensions: []
38
21
  extra_rdoc_files: []
39
22
  files:
@@ -49,19 +32,13 @@ files:
49
32
  - README.md
50
33
  - Rakefile
51
34
  - basketball.gemspec
52
- - exe/basketball
53
- - exe/basketball-draft-room
54
- - exe/basketball-season-coordinator
55
35
  - lib/basketball.rb
56
36
  - lib/basketball/app.rb
57
- - lib/basketball/app/coordinator_cli.rb
58
37
  - lib/basketball/app/coordinator_repository.rb
59
38
  - lib/basketball/app/document_repository.rb
60
39
  - lib/basketball/app/file_store.rb
61
40
  - lib/basketball/app/in_memory_store.rb
62
41
  - lib/basketball/app/league_repository.rb
63
- - lib/basketball/app/league_serializable.rb
64
- - lib/basketball/app/room_cli.rb
65
42
  - lib/basketball/app/room_repository.rb
66
43
  - lib/basketball/draft.rb
67
44
  - lib/basketball/draft/assessment.rb
data/exe/basketball DELETED
@@ -1,91 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require 'bundler/setup'
5
- require 'basketball'
6
-
7
- # This is an example integration script tying all the contexts together.
8
- # This is definitely subject to change as this library gets extended further.
9
-
10
- store = Basketball::App::FileStore.new
11
- room_repository = Basketball::App::RoomRepository.new(store)
12
- league_repository = Basketball::App::LeagueRepository.new(store)
13
- coordinator_repository = Basketball::App::CoordinatorRepository.new(store)
14
- scheduler = Basketball::Season::Scheduler.new
15
-
16
- seed_path = ARGV[0].to_s.empty? ? File.join('spec', 'fixtures', 'seed.json') : ARGV[0]
17
- output_dir = ARGV[1].to_s.empty? ? 'tmp' : ARGV[1]
18
- seed = JSON.parse(File.read(seed_path), symbolize_names: true)
19
-
20
- league_path = File.join(output_dir, '00-league.json')
21
-
22
- File.write(league_path, seed[:league].to_json)
23
-
24
- front_offices = seed.dig(:league, :conferences).flat_map do |c|
25
- c[:divisions].flat_map do |d|
26
- d[:teams].flat_map do |t|
27
- { id: t[:id] }
28
- end
29
- end
30
- end
31
-
32
- room = {
33
- rounds: 12,
34
- front_offices:,
35
- players: seed[:players]
36
- }
37
-
38
- room_input_path = File.join(output_dir, '01-room-input.json')
39
-
40
- File.write(room_input_path, room.to_json)
41
-
42
- room = room_repository.load(room_input_path)
43
-
44
- room.sim_rest!
45
-
46
- room_output_path = File.join(output_dir, '02-room-output.json')
47
-
48
- room_repository.save(room_output_path, room)
49
-
50
- league = league_repository.load(league_path)
51
-
52
- room.teams.each do |team|
53
- team.players.each do |player|
54
- league.sign!(team:, player:)
55
- end
56
- end
57
-
58
- scheduler_input_path = File.join(output_dir, '03-scheduler-input.json')
59
-
60
- league_repository.save(scheduler_input_path, league)
61
-
62
- calendar = scheduler.schedule(league:)
63
- current_date = calendar.exhibition_start_date
64
- coordinator = Basketball::Season::Coordinator.new(league:, calendar:, current_date:)
65
-
66
- coordinator_input_path = File.join(output_dir, '04-coordinator-input.json')
67
-
68
- coordinator_repository.save(coordinator_input_path, coordinator)
69
-
70
- coordinator.sim_rest!
71
-
72
- coordinator_output_path = File.join(output_dir, '05-coordinator-output.json')
73
-
74
- coordinator_repository.save(coordinator_output_path, coordinator)
75
-
76
- preseason_standings = Basketball::Season::Standings.new
77
- season_standings = Basketball::Season::Standings.new
78
-
79
- league.teams.each do |team|
80
- preseason_standings.register!(team)
81
- season_standings.register!(team)
82
- end
83
-
84
- coordinator.exhibition_results.each { |result| preseason_standings.accept!(result) }
85
- coordinator.regular_results.each { |result| season_standings.accept!(result) }
86
-
87
- puts 'Preseason Standings'
88
- puts preseason_standings
89
- puts '--------------------------------------------'
90
- puts 'Season Standings'
91
- puts season_standings
@@ -1,7 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require 'bundler/setup'
5
- require 'basketball'
6
-
7
- Basketball::App::RoomCLI.new(args: ARGV).invoke!
@@ -1,7 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require 'bundler/setup'
5
- require 'basketball'
6
-
7
- Basketball::App::CoordinatorCLI.new(args: ARGV).invoke!
@@ -1,227 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Basketball
4
- module App
5
- # Examples:
6
- # exe/basketball-season-coordinator -o tmp/coordinator.json
7
- # exe/basketball-season-coordinator -i tmp/coordinator.json -o tmp/coordinator2.json -d 1
8
- # exe/basketball-season-coordinator -i tmp/coordinator2.json -e
9
- # exe/basketball-season-coordinator -i tmp/coordinator2.json -o tmp/coordinator2.json -d 2
10
- # exe/basketball-season-coordinator -i tmp/coordinator2.json -a
11
- #
12
- # exe/basketball-season-coordinator -o tmp/coordinator.json -ae
13
- class CoordinatorCLI
14
- attr_reader :opts, :io, :coordinator_repository
15
-
16
- def initialize(
17
- args:,
18
- io: $stdout,
19
- coordinator_repository: CoordinatorRepository.new(FileStore.new)
20
- )
21
- raise ArgumentError, 'coordinator_repository is required' unless coordinator_repository
22
- raise ArgumentError, 'io is required' unless io
23
-
24
- @io = io
25
- @opts = slop_parse(args)
26
- @coordinator_repository = coordinator_repository
27
-
28
- if no_input? && no_output?
29
- io.puts('Input and/or output paths are required.')
30
-
31
- exit
32
- end
33
-
34
- freeze
35
- end
36
-
37
- def invoke!
38
- coordinator = read
39
-
40
- execute(coordinator)
41
- output_status(coordinator)
42
- write(coordinator)
43
- events(coordinator)
44
-
45
- self
46
- end
47
-
48
- private
49
-
50
- def output_status(coordinator)
51
- io.puts
52
- io.puts('Status')
53
-
54
- if coordinator.done?
55
- io.puts('Coordinator is complete!')
56
- else
57
- io.puts("#{coordinator.days_left} Remaining day(s) (#{coordinator.total_days} total)")
58
- io.puts("Currently on: #{coordinator.current_date}")
59
- io.puts("#{coordinator.exhibitions_left} Remaining exhibition (#{coordinator.total_exhibitions} total)")
60
- io.puts("#{coordinator.regulars_left} Remaining season (#{coordinator.total_regulars} total)")
61
- end
62
- end
63
-
64
- def execute(coordinator)
65
- event_count = 0
66
-
67
- io.puts
68
- io.puts('New Events')
69
-
70
- days&.times do
71
- coordinator.sim! do |event|
72
- io.puts(event)
73
-
74
- event_count += 1
75
- end
76
- end
77
-
78
- if sim_all
79
- coordinator.sim_rest! do |event|
80
- io.puts(event)
81
-
82
- event_count += 1
83
- end
84
- end
85
-
86
- io.puts("Generated #{event_count} new event(s)")
87
-
88
- nil
89
- end
90
-
91
- def days
92
- opts[:days]
93
- end
94
-
95
- def sim_all
96
- opts[:sim_all]
97
- end
98
-
99
- def write(coordinator)
100
- path = output? ? output : input
101
-
102
- coordinator_repository.save(path, coordinator)
103
-
104
- path
105
- end
106
-
107
- def make_league
108
- Org::League.new(
109
- conferences: 2.times.map do |i|
110
- conference_id = "C#{i}"
111
-
112
- Org::Conference.new(
113
- id: conference_id,
114
- divisions: 3.times.map do |j|
115
- division_id = "#{conference_id}-D#{j}"
116
-
117
- Org::Division.new(
118
- id: division_id,
119
- teams: 5.times.map do |k|
120
- team_id = "#{division_id}-T#{k}"
121
-
122
- Org::Team.new(
123
- id: team_id,
124
- players: 12.times.map do |l|
125
- player_id = "#{team_id}-P#{l}"
126
-
127
- Org::Player.new(
128
- id: player_id,
129
- overall: rand(20..100),
130
- position: Org::Position.random
131
- )
132
- end
133
- )
134
- end
135
- )
136
- end
137
- )
138
- end
139
- )
140
- end
141
-
142
- def make_coordinator
143
- league = make_league
144
- year = Time.now.year
145
- calendar = Season::Scheduler.new.schedule(league:, year:)
146
- current_date = calendar.games.min_by(&:date).date
147
-
148
- Season::Coordinator.new(
149
- calendar:,
150
- current_date:,
151
- league:
152
- )
153
- end
154
-
155
- def read
156
- coordinator =
157
- if input?
158
- io.puts("Coordinator loaded from: #{input}")
159
-
160
- coordinator_repository.load(input)
161
- else
162
- io.puts('Input path was not provided, generating fresh coordinator')
163
-
164
- make_coordinator
165
- end
166
-
167
- io.puts("Current Date: #{coordinator.current_date}")
168
-
169
- coordinator
170
- end
171
-
172
- def input
173
- opts[:input]
174
- end
175
-
176
- def input?
177
- !no_input?
178
- end
179
-
180
- def no_input?
181
- input.to_s.empty?
182
- end
183
-
184
- def output
185
- opts[:output]
186
- end
187
-
188
- def no_output?
189
- output.to_s.empty?
190
- end
191
-
192
- def output?
193
- !no_output?
194
- end
195
-
196
- def events(coordinator)
197
- return unless opts[:events]
198
-
199
- io.puts
200
- io.puts('Event Log')
201
-
202
- puts coordinator.results
203
- end
204
-
205
- def slop_parse(args)
206
- Slop.parse(args) do |o|
207
- o.banner = 'Usage: basketball-season-coordinator [options] ...'
208
-
209
- input_description = <<~DESC
210
- Path to load the Coordinator from. If omitted then a new coordinator will be created.
211
- DESC
212
-
213
- o.string '-i', '--input', input_description.chomp
214
- o.string '-o', '--output', 'Path to save updated coordinator. If omitted then the input path will be used.'
215
- o.integer '-d', '--days', 'Number of days to simulate'
216
- o.bool '-a', '--sim-all', 'Simulate the rest of the coordinator', default: false
217
- o.bool '-e', '--events', 'Output event log.', default: false
218
-
219
- o.on '-h', '--help', 'Print out help, like this is doing right now.' do
220
- io.puts(o)
221
- exit
222
- end
223
- end.to_h
224
- end
225
- end
226
- end
227
- end
@@ -1,99 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Basketball
4
- module App
5
- # Provides methods to serialize/deserialize a League object
6
- module LeagueSerializable
7
- # Serialization
8
-
9
- def serialize_league(league)
10
- {
11
- id: league.id,
12
- conferences: serialize_conferences(league.conferences)
13
- }
14
- end
15
-
16
- def serialize_conferences(conferences)
17
- conferences.map do |conference|
18
- {
19
- id: conference.id,
20
- divisions: serialize_divisions(conference.divisions)
21
- }
22
- end
23
- end
24
-
25
- def serialize_divisions(divisions)
26
- divisions.map do |division|
27
- {
28
- id: division.id,
29
- teams: serialize_teams(division.teams)
30
- }
31
- end
32
- end
33
-
34
- def serialize_teams(teams)
35
- teams.map do |team|
36
- {
37
- id: team.id,
38
- players: serialize_players(team.players)
39
- }
40
- end
41
- end
42
-
43
- def serialize_players(players)
44
- players.map do |player|
45
- {
46
- id: player.id,
47
- overall: player.overall,
48
- position: player.position&.code
49
- }
50
- end
51
- end
52
-
53
- # Deserialization
54
-
55
- def deserialize_league(league_hash)
56
- Org::League.new(
57
- conferences: deserialize_conferences(league_hash[:conferences])
58
- )
59
- end
60
-
61
- def deserialize_conferences(conference_hashes)
62
- (conference_hashes || []).map do |conference_hash|
63
- Org::Conference.new(
64
- id: conference_hash[:id],
65
- divisions: deserialize_divisions(conference_hash[:divisions])
66
- )
67
- end
68
- end
69
-
70
- def deserialize_divisions(division_hashes)
71
- (division_hashes || []).map do |division_hash|
72
- Org::Division.new(
73
- id: division_hash[:id],
74
- teams: deserialize_teams(division_hash[:teams])
75
- )
76
- end
77
- end
78
-
79
- def deserialize_teams(team_hashes)
80
- (team_hashes || []).map do |team_hash|
81
- Org::Team.new(
82
- id: team_hash[:id],
83
- players: deserialize_players(team_hash[:players])
84
- )
85
- end
86
- end
87
-
88
- def deserialize_players(player_hashes)
89
- (player_hashes || []).map do |player_hash|
90
- Org::Player.new(
91
- id: player_hash[:id],
92
- overall: player_hash[:overall],
93
- position: Org::Position.new(player_hash[:position])
94
- )
95
- end
96
- end
97
- end
98
- end
99
- end
@@ -1,216 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Basketball
4
- module App
5
- # Examples:
6
- # exe/basketball-draft-room -o tmp/draft.json
7
- # exe/basketball-draft-room -i tmp/draft.json -o tmp/draft-wip.json -s 26 -p P-5,P-10 -l 10
8
- # exe/basketball-draft-room -i tmp/draft-wip.json -x 2
9
- # exe/basketball-draft-room -i tmp/draft-wip.json -g -l 10
10
- # exe/basketball-draft-room -i tmp/draft-wip.json -s 30 -l 10
11
- # exe/basketball-draft-room -i tmp/draft-wip.json -ate
12
- class RoomCLI
13
- class PlayerNotFound < StandardError; end
14
-
15
- attr_reader :opts,
16
- :io,
17
- :room_repository
18
-
19
- def initialize(
20
- args:,
21
- io: $stdout,
22
- room_repository: RoomRepository.new(FileStore.new)
23
- )
24
- @io = io
25
- @opts = slop_parse(args)
26
- @room_repository = room_repository
27
-
28
- if opts[:input].to_s.empty? && opts[:output].to_s.empty?
29
- io.puts('Input and/or output paths are required.')
30
-
31
- exit
32
- end
33
-
34
- freeze
35
- end
36
-
37
- def invoke!
38
- room = load_room
39
-
40
- execute(room)
41
- status(room)
42
- write(room)
43
- events(room)
44
- teams(room)
45
- query(room)
46
-
47
- self
48
- end
49
-
50
- private
51
-
52
- def status(room)
53
- io.puts
54
- io.puts('Status')
55
-
56
- if room.done?
57
- io.puts('Draft is complete!')
58
- else
59
- round = room.round
60
- round_pick = room.round_pick
61
- front_office = room.front_office
62
-
63
- io.puts("#{room.remaining_picks} Remaining pick(s)")
64
- io.puts("Up Next: Round #{round} pick #{round_pick} for #{front_office}")
65
- end
66
- end
67
-
68
- def slop_parse(args)
69
- Slop.parse(args) do |o|
70
- o.banner = 'Usage: basketball-draft-room [options] ...'
71
-
72
- o.string '-i', '--input', 'Path to load the Room from. If omitted then a new draft will be generated.'
73
- o.string '-o', '--output', 'Path to write the room to (if omitted then input path will be used)'
74
- o.integer '-s', '--simulate', 'Number of picks to simulate (default is 0).', default: 0
75
- o.bool '-a', '--simulate-all', 'Simulate the rest of the draft', default: false
76
- o.array '-p', '--picks', 'Comma-separated list of ordered player IDs to pick.', delimiter: ','
77
- o.integer '-l', '--list', 'List the top rated available players (default is 0).', default: 0
78
- o.bool '-t', '--teams', 'Output all teams and their picks', default: false
79
- o.integer '-x', '--skip', 'Number of picks to skip (default is 0).', default: 0
80
- o.bool '-e', '--events', 'Output event log.', default: false
81
-
82
- o.on '-h', '--help', 'Print out help, like this is doing right now.' do
83
- io.puts(o)
84
- exit
85
- end
86
- end.to_h
87
- end
88
-
89
- def load_room
90
- if opts[:input].to_s.empty?
91
- io.puts('Input path was not provided, generating fresh front_offices and players')
92
-
93
- generate_draft
94
- else
95
- io.puts("Draft loaded from: #{opts[:input]}")
96
-
97
- read
98
- end
99
- end
100
-
101
- def generate_draft
102
- front_offices = 30.times.map do |i|
103
- Draft::FrontOffice.new(
104
- id: "T-#{i + 1}"
105
- )
106
- end
107
-
108
- players = 450.times.map do |i|
109
- Org::Player.new(
110
- id: "P-#{i + 1}",
111
- overall: (20..100).to_a.sample,
112
- position: Org::Position.random
113
- )
114
- end
115
-
116
- Draft::Room.new(rounds: 12, players:, front_offices:)
117
- end
118
-
119
- def teams(room)
120
- return unless opts[:teams]
121
-
122
- io.puts
123
- io.puts(room.teams)
124
- end
125
-
126
- def events(room)
127
- return unless opts[:events]
128
-
129
- io.puts
130
- io.puts('Event Log')
131
-
132
- puts room.events
133
- end
134
-
135
- def query(room)
136
- list = opts[:list]
137
-
138
- return if list <= 0
139
-
140
- players = room.undrafted_players.sort_by(&:overall).reverse.take(opts[:list])
141
-
142
- io.puts
143
- io.puts("Top #{list} available players")
144
- io.puts(players)
145
- end
146
-
147
- def read
148
- room_repository.load(opts[:input])
149
- end
150
-
151
- # rubocop:disable Metrics/AbcSize
152
- def execute(room)
153
- event_count = 0
154
-
155
- io.puts
156
- io.puts('New Events')
157
-
158
- (opts[:picks] || []).each do |id|
159
- break if room.done?
160
-
161
- player = room.players.find { |p| p.id == id.to_s.upcase }
162
-
163
- raise PlayerNotFound, "player not found by id: #{id}" unless player
164
-
165
- event = room.pick!(player)
166
-
167
- io.puts(event)
168
-
169
- event_count += 1
170
- end
171
-
172
- opts[:skip].times do
173
- event = room.skip!
174
-
175
- io.puts(event)
176
-
177
- event_count += 1
178
- end
179
-
180
- opts[:simulate].times do
181
- room.sim!
182
-
183
- event_count += 1
184
- end
185
-
186
- if opts[:simulate_all]
187
- room.sim_rest! do |event|
188
- io.puts(event)
189
-
190
- event_count += 1
191
- end
192
- end
193
-
194
- io.puts("Generated #{event_count} new event(s)")
195
-
196
- nil
197
- end
198
- # rubocop:enable Metrics/AbcSize
199
-
200
- def output_default_to_input
201
- opts[:output].to_s.empty? ? opts[:input] : opts[:output]
202
- end
203
-
204
- def write(room)
205
- output = output_default_to_input
206
-
207
- room_repository.save(output, room)
208
-
209
- io.puts
210
- io.puts("Draft written to: #{output}")
211
-
212
- nil
213
- end
214
- end
215
- end
216
- end