basketball 0.0.8 → 0.0.9

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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +9 -19
  3. data/CHANGELOG.md +1 -39
  4. data/README.md +72 -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 +243 -0
  9. data/lib/basketball/app/coordinator_repository.rb +191 -0
  10. data/lib/basketball/app/file_store.rb +22 -0
  11. data/lib/basketball/{draft/cli.rb → app/room_cli.rb} +53 -76
  12. data/lib/basketball/app/room_repository.rb +189 -0
  13. data/lib/basketball/app.rb +12 -0
  14. data/lib/basketball/draft/assessment.rb +31 -0
  15. data/lib/basketball/draft/event.rb +3 -2
  16. data/lib/basketball/draft/front_office.rb +35 -28
  17. data/lib/basketball/draft/{pick_event.rb → pick.rb} +13 -6
  18. data/lib/basketball/draft/room.rb +119 -119
  19. data/lib/basketball/draft/{player_search.rb → scout.rb} +4 -9
  20. data/lib/basketball/draft/skip.rb +12 -0
  21. data/lib/basketball/draft.rb +13 -6
  22. data/lib/basketball/entity.rb +10 -4
  23. data/lib/basketball/org/league.rb +73 -0
  24. data/lib/basketball/org/player.rb +26 -0
  25. data/lib/basketball/{draft → org}/position.rb +3 -2
  26. data/lib/basketball/org/team.rb +38 -0
  27. data/lib/basketball/org.rb +12 -0
  28. data/lib/basketball/season/arena.rb +112 -0
  29. data/lib/basketball/season/calendar.rb +41 -72
  30. data/lib/basketball/season/coordinator.rb +185 -126
  31. data/lib/basketball/season/{preseason_game.rb → exhibition.rb} +2 -1
  32. data/lib/basketball/season/game.rb +15 -10
  33. data/lib/basketball/season/matchup.rb +27 -0
  34. data/lib/basketball/season/opponent.rb +15 -0
  35. data/lib/basketball/season/{season_game.rb → regular.rb} +2 -1
  36. data/lib/basketball/season/result.rb +37 -0
  37. data/lib/basketball/season.rb +12 -13
  38. data/lib/basketball/value_object.rb +4 -1
  39. data/lib/basketball/version.rb +1 -1
  40. data/lib/basketball.rb +9 -4
  41. metadata +32 -44
  42. data/lib/basketball/draft/league.rb +0 -70
  43. data/lib/basketball/draft/player.rb +0 -43
  44. data/lib/basketball/draft/room_serializer.rb +0 -186
  45. data/lib/basketball/draft/roster.rb +0 -37
  46. data/lib/basketball/draft/sim_event.rb +0 -23
  47. data/lib/basketball/draft/skip_event.rb +0 -13
  48. data/lib/basketball/season/calendar_serializer.rb +0 -94
  49. data/lib/basketball/season/conference.rb +0 -57
  50. data/lib/basketball/season/division.rb +0 -43
  51. data/lib/basketball/season/league.rb +0 -114
  52. data/lib/basketball/season/league_serializer.rb +0 -99
  53. data/lib/basketball/season/scheduling_cli.rb +0 -198
  54. data/lib/basketball/season/team.rb +0 -21
@@ -1,114 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Basketball
4
- module Season
5
- class League
6
- class UnknownTeamError < StandardError; end
7
-
8
- class << self
9
- def generate_random; end
10
- end
11
-
12
- CONFERENCES_SIZE = 2
13
-
14
- attr_reader :conferences
15
-
16
- def initialize(conferences: [])
17
- @conferences = []
18
-
19
- conferences.each { |c| register_conference!(c) }
20
-
21
- if conferences.length != CONFERENCES_SIZE
22
- raise BadConferencesSizeError, "there has to be #{CONFERENCES_SIZE} conferences"
23
- end
24
-
25
- freeze
26
- end
27
-
28
- def to_s
29
- (['League'] + conferences.map(&:to_s)).join("\n")
30
- end
31
-
32
- def divisions
33
- conferences.flat_map(&:divisions)
34
- end
35
-
36
- def conference?(conference)
37
- conferences.include?(conference)
38
- end
39
-
40
- def division?(division)
41
- divisions.include?(division)
42
- end
43
-
44
- def team?(team)
45
- teams.include?(team)
46
- end
47
-
48
- def teams
49
- conferences.flat_map do |conference|
50
- conference.divisions.flat_map(&:teams)
51
- end
52
- end
53
-
54
- def conference_for(team)
55
- conferences.find { |c| c.divisions.find { |d| d.teams.include?(team) } }
56
- end
57
-
58
- def division_for(team)
59
- conference_for(team)&.divisions&.find { |d| d.teams.include?(team) }
60
- end
61
-
62
- # Same conference, same division
63
- def division_opponents_for(team)
64
- division = division_for(team)
65
-
66
- return nil unless division
67
-
68
- division.teams - [team]
69
- end
70
-
71
- # Same conference, different division
72
- def cross_division_opponents_for(team)
73
- conference = conference_for(team)
74
- division = division_for(team)
75
-
76
- return nil unless conference && division
77
-
78
- other_divisions = conference.divisions - [division]
79
-
80
- other_divisions.flat_map(&:teams)
81
- end
82
-
83
- # Different conference
84
- def cross_conference_opponents_for(team)
85
- conference = conference_for(team)
86
-
87
- return nil unless conference
88
-
89
- other_conferences = conferences - [conference]
90
-
91
- other_conferences.flat_map { |c| c.divisions.flat_map(&:teams) }
92
- end
93
-
94
- private
95
-
96
- def register_conference!(conference)
97
- raise ArgumentError, 'conference is required' unless conference
98
- raise ConferenceAlreadyRegisteredError, "#{conference} already registered" if conference?(conference)
99
-
100
- conference.divisions.each do |division|
101
- raise DivisionAlreadyRegisteredError, "#{division} already registered" if division?(division)
102
-
103
- division.teams.each do |team|
104
- raise TeamAlreadyRegisteredError, "#{team} already registered" if team?(team)
105
- end
106
- end
107
-
108
- conferences << conference
109
-
110
- self
111
- end
112
- end
113
- end
114
- end
@@ -1,99 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Basketball
4
- module Season
5
- class LeagueSerializer
6
- def to_hash(league)
7
- {
8
- 'conferences' => serialize_conferences(league.conferences)
9
- }
10
- end
11
-
12
- def from_hash(json)
13
- conferences = deserialize_conferences(json['conferences'])
14
-
15
- League.new(conferences:)
16
- end
17
-
18
- def deserialize(string)
19
- json = JSON.parse(string)
20
-
21
- from_hash(json)
22
- end
23
-
24
- def serialize(league)
25
- to_hash(league).to_json
26
- end
27
-
28
- private
29
-
30
- ## Deserialization
31
-
32
- def deserialize_conferences(conferences)
33
- (conferences || []).map do |conference_id, conference_hash|
34
- Conference.new(
35
- id: conference_id,
36
- name: conference_hash['name'],
37
- divisions: deserialize_divisions(conference_hash['divisions'])
38
- )
39
- end
40
- end
41
-
42
- def deserialize_divisions(divisions)
43
- (divisions || []).map do |division_id, division_hash|
44
- Division.new(
45
- id: division_id,
46
- name: division_hash['name'],
47
- teams: deserialize_teams(division_hash['teams'])
48
- )
49
- end
50
- end
51
-
52
- def deserialize_teams(teams)
53
- (teams || []).map do |team_id, team_hash|
54
- Team.new(
55
- id: team_id,
56
- name: team_hash['name']
57
- )
58
- end
59
- end
60
-
61
- ## Serialization
62
-
63
- def serialize_conferences(conferences)
64
- conferences.to_h do |conference|
65
- [
66
- conference.id,
67
- {
68
- 'name' => conference.name,
69
- 'divisions' => serialize_divisions(conference.divisions)
70
- }
71
- ]
72
- end
73
- end
74
-
75
- def serialize_divisions(divisions)
76
- divisions.to_h do |division|
77
- [
78
- division.id,
79
- {
80
- 'name' => division.name,
81
- 'teams' => serialize_teams(division.teams)
82
- }
83
- ]
84
- end
85
- end
86
-
87
- def serialize_teams(teams)
88
- teams.to_h do |team|
89
- [
90
- team.id,
91
- {
92
- 'name' => team.name
93
- }
94
- ]
95
- end
96
- end
97
- end
98
- end
99
- end
@@ -1,198 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'calendar_serializer'
4
- require_relative 'conference'
5
- require_relative 'coordinator'
6
- require_relative 'division'
7
- require_relative 'league'
8
- require_relative 'league_serializer'
9
- require_relative 'team'
10
-
11
- module Basketball
12
- module Season
13
- # Examples:
14
- # exe/basketball-season-scheduling -o tmp/league.json
15
- # exe/basketball-season-scheduling -i tmp/league.json -o tmp/calendar.json
16
- # exe/basketball-season-scheduling -i tmp/league.json -o tmp/calendar.json -y 2005
17
- # exe/basketball-season-scheduling -c tmp/calendar.json
18
- # exe/basketball-season-scheduling -c tmp/calendar.json -t C0-D0-T0
19
- # exe/basketball-season-scheduling -c tmp/calendar.json -d 2005-02-03
20
- # exe/basketball-season-scheduling -c tmp/calendar.json -d 2005-02-03 -t C0-D0-T0
21
- class SchedulingCLI
22
- attr_reader :opts,
23
- :league_serializer,
24
- :calendar_serializer,
25
- :io,
26
- :coordinator
27
-
28
- def initialize(args:, io: $stdout)
29
- @io = io
30
- @opts = slop_parse(args)
31
- @league_serializer = LeagueSerializer.new
32
- @calendar_serializer = CalendarSerializer.new
33
- @coordinator = Coordinator.new
34
-
35
- freeze
36
- end
37
-
38
- def invoke!
39
- if output?
40
- out_dir = File.dirname(output)
41
- FileUtils.mkdir_p(out_dir)
42
- end
43
-
44
- if output? && no_input?
45
- execute_with_no_input
46
- elsif output?
47
- execute_with_input
48
- end
49
-
50
- output_cal_query if cal
51
-
52
- self
53
- end
54
-
55
- private
56
-
57
- def output_cal_query
58
- contents = File.read(cal)
59
- calendar = calendar_serializer.deserialize(contents)
60
- team_instance = team ? calendar.team(team) : nil
61
- games = calendar.games_for(date:, team: team_instance).sort_by(&:date)
62
- pre_counter = 1
63
- counter = 1
64
-
65
- io.puts("Games for [team: #{team}, date: #{date}]")
66
- games.each do |game|
67
- if game.is_a?(PreseasonGame)
68
- io.puts("##{pre_counter} - #{game}")
69
- pre_counter += 1
70
- else
71
- io.puts("##{counter} - #{game}")
72
- counter += 1
73
- end
74
- end
75
- end
76
-
77
- def execute_with_input
78
- io.puts("Loading league from: #{input}")
79
-
80
- contents = File.read(input)
81
- league = league_serializer.deserialize(contents)
82
-
83
- io.puts("Generating calendar for the year #{year}...")
84
-
85
- calendar = coordinator.schedule(league:, year:)
86
- contents = calendar_serializer.serialize(calendar)
87
-
88
- File.write(output, contents)
89
-
90
- io.puts("Calendar written to: #{output}")
91
- end
92
-
93
- def execute_with_no_input
94
- league = generate_league
95
- contents = league_serializer.serialize(league)
96
-
97
- File.write(output, contents)
98
-
99
- io.puts("League written to: #{output}")
100
- end
101
-
102
- def cal
103
- opts[:cal].to_s.empty? ? nil : opts[:cal]
104
- end
105
-
106
- def team
107
- opts[:team].to_s.empty? ? nil : opts[:team]
108
- end
109
-
110
- def date
111
- opts[:date].to_s.empty? ? nil : Date.parse(opts[:date])
112
- end
113
-
114
- def year
115
- opts[:year].to_s.empty? ? Date.today.year : opts[:year]
116
- end
117
-
118
- def no_input?
119
- input.to_s.empty?
120
- end
121
-
122
- def input
123
- opts[:input]
124
- end
125
-
126
- def output?
127
- !output.to_s.empty?
128
- end
129
-
130
- def output
131
- opts[:output]
132
- end
133
-
134
- def generate_conferences
135
- 2.times.map do |i|
136
- id = "C#{i}"
137
-
138
- Conference.new(
139
- id:,
140
- name: Faker::Esport.league,
141
- divisions: generate_divisions("#{id}-")
142
- )
143
- end
144
- end
145
-
146
- def generate_divisions(id_prefix)
147
- 3.times.map do |j|
148
- id = "#{id_prefix}D#{j}"
149
-
150
- Division.new(
151
- id:,
152
- name: Faker::Address.community,
153
- teams: generate_teams("#{id}-")
154
- )
155
- end
156
- end
157
-
158
- def generate_teams(id_prefix)
159
- 5.times.map do |k|
160
- Team.new(
161
- id: "#{id_prefix}T#{k}",
162
- name: Faker::Team.name
163
- )
164
- end
165
- end
166
-
167
- def generate_league
168
- League.new(conferences: generate_conferences)
169
- end
170
-
171
- def slop_parse(args)
172
- Slop.parse(args) do |o|
173
- o.banner = 'Usage: basketball-schedule [options] ...'
174
-
175
- output_description = <<~DESC
176
- If input path is omitted then a new league will be written to this path.
177
- If an input path is specified then a Calendar will be written to the output path.
178
- DESC
179
-
180
- # League and Calendar Generation Interface
181
- o.string '-i', '--input', 'Path to load the League from. If omitted then a new league will be generated.'
182
- o.string '-o', '--output', output_description
183
- o.integer '-y', '--year', 'Year to use to generate a calendar for (defaults to current year).'
184
-
185
- # Calendar Query Interface
186
- o.string '-c', '--cal', 'Path to load a Calendar from. If omitted then no matchups will be outputted.'
187
- o.string '-d', '--date', 'Filter matchups to just the date specified (requires --cal option).'
188
- o.string '-t', '--team', 'Filter matchups to just the team ID specified (requires --cal option).'
189
-
190
- o.on '-h', '--help', 'Print out help, like this is doing right now.' do
191
- io.puts(o)
192
- exit
193
- end
194
- end.to_h
195
- end
196
- end
197
- end
198
- end
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Basketball
4
- module Season
5
- class Team < Entity
6
- attr_reader :name
7
-
8
- def initialize(id:, name: '')
9
- super(id)
10
-
11
- @name = name.to_s
12
-
13
- freeze
14
- end
15
-
16
- def to_s
17
- "[#{super}] #{name}"
18
- end
19
- end
20
- end
21
- end