basketball 0.0.14 → 0.0.15

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: 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