basketball 0.0.6 → 0.0.8

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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +20 -7
  3. data/Gemfile +11 -0
  4. data/README.md +49 -41
  5. data/basketball.gemspec +2 -13
  6. data/exe/basketball-draft +1 -1
  7. data/exe/{basketball-schedule → basketball-season-scheduling} +1 -1
  8. data/lib/basketball/{drafting → draft}/cli.rb +34 -34
  9. data/lib/basketball/{drafting → draft}/event.rb +1 -1
  10. data/lib/basketball/{drafting → draft}/front_office.rb +1 -1
  11. data/lib/basketball/{drafting → draft}/league.rb +1 -1
  12. data/lib/basketball/{drafting → draft}/pick_event.rb +1 -1
  13. data/lib/basketball/{drafting → draft}/player.rb +1 -1
  14. data/lib/basketball/{drafting → draft}/player_search.rb +1 -1
  15. data/lib/basketball/{drafting → draft}/position.rb +1 -1
  16. data/lib/basketball/{drafting/engine.rb → draft/room.rb} +2 -2
  17. data/lib/basketball/draft/room_serializer.rb +186 -0
  18. data/lib/basketball/{drafting → draft}/roster.rb +1 -1
  19. data/lib/basketball/{drafting → draft}/sim_event.rb +1 -1
  20. data/lib/basketball/{drafting → draft}/skip_event.rb +1 -1
  21. data/lib/basketball/{drafting.rb → draft.rb} +2 -2
  22. data/lib/basketball/{scheduling → season}/calendar.rb +1 -1
  23. data/lib/basketball/{scheduling → season}/calendar_serializer.rb +31 -21
  24. data/lib/basketball/{scheduling → season}/conference.rb +1 -1
  25. data/lib/basketball/{scheduling → season}/coordinator.rb +2 -2
  26. data/lib/basketball/{scheduling → season}/division.rb +1 -1
  27. data/lib/basketball/{scheduling → season}/game.rb +1 -1
  28. data/lib/basketball/{scheduling → season}/league.rb +1 -1
  29. data/lib/basketball/{scheduling → season}/league_serializer.rb +26 -17
  30. data/lib/basketball/{scheduling → season}/preseason_game.rb +1 -1
  31. data/lib/basketball/{scheduling/cli.rb → season/scheduling_cli.rb} +9 -9
  32. data/lib/basketball/{scheduling → season}/season_game.rb +1 -1
  33. data/lib/basketball/{scheduling → season}/team.rb +1 -1
  34. data/lib/basketball/{scheduling.rb → season.rb} +2 -2
  35. data/lib/basketball/version.rb +1 -1
  36. data/lib/basketball.rb +2 -2
  37. metadata +32 -172
  38. data/lib/basketball/drafting/engine_serializer.rb +0 -174
@@ -0,0 +1,186 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'front_office'
4
+ require_relative 'player'
5
+ require_relative 'pick_event'
6
+ require_relative 'sim_event'
7
+ require_relative 'skip_event'
8
+
9
+ module Basketball
10
+ module Draft
11
+ class RoomSerializer
12
+ EVENT_CLASSES = {
13
+ 'PickEvent' => PickEvent,
14
+ 'SimEvent' => SimEvent,
15
+ 'SkipEvent' => SkipEvent
16
+ }.freeze
17
+
18
+ private_constant :EVENT_CLASSES
19
+
20
+ def to_hash(room)
21
+ {
22
+ 'info' => serialize_info(room),
23
+ 'room' => serialize_room(room),
24
+ 'league' => serialize_league(room)
25
+ }
26
+ end
27
+
28
+ def from_hash(json)
29
+ front_offices = deserialize_front_offices(json)
30
+ players = deserialize_players(json)
31
+ events = deserialize_events(json, players, front_offices)
32
+
33
+ room_opts = {
34
+ players:,
35
+ front_offices:,
36
+ events:
37
+ }
38
+
39
+ room_opts[:rounds] = json.dig('room', 'rounds') if json.dig('room', 'rounds')
40
+
41
+ Room.new(**room_opts)
42
+ end
43
+
44
+ def deserialize(string)
45
+ json = JSON.parse(string)
46
+
47
+ from_hash(json)
48
+ end
49
+
50
+ def serialize(room)
51
+ to_hash(room).to_json
52
+ end
53
+
54
+ private
55
+
56
+ def serialize_room(room)
57
+ {
58
+ 'rounds' => room.rounds,
59
+ 'front_offices' => serialize_front_offices(room),
60
+ 'players' => serialize_players(room),
61
+ 'events' => serialize_events(room.events)
62
+ }
63
+ end
64
+
65
+ def serialize_info(room)
66
+ {
67
+ 'total_picks' => room.total_picks,
68
+ 'current_round' => room.current_round,
69
+ 'current_round_pick' => room.current_round_pick,
70
+ 'current_front_office' => room.current_front_office&.id,
71
+ 'current_pick' => room.current_pick,
72
+ 'remaining_picks' => room.remaining_picks,
73
+ 'done' => room.done?
74
+ }
75
+ end
76
+
77
+ def serialize_league(room)
78
+ league = room.to_league
79
+
80
+ rosters = league.rosters.to_h do |roster|
81
+ [
82
+ roster.id,
83
+ {
84
+ 'players' => roster.players.map(&:id)
85
+ }
86
+ ]
87
+ end
88
+
89
+ {
90
+ 'free_agents' => league.free_agents.map(&:id),
91
+ 'rosters' => rosters
92
+ }
93
+ end
94
+
95
+ def serialize_front_offices(room)
96
+ room.front_offices.to_h do |front_office|
97
+ [
98
+ front_office.id,
99
+ {
100
+ 'name' => front_office.name,
101
+ 'fuzz' => front_office.fuzz,
102
+ 'depth' => front_office.depth,
103
+ 'prioritized_positions' => front_office.prioritized_positions.map(&:code)
104
+ }
105
+ ]
106
+ end
107
+ end
108
+
109
+ def serialize_players(room)
110
+ room.players.to_h do |player|
111
+ [
112
+ player.id,
113
+ {
114
+ 'first_name' => player.first_name,
115
+ 'last_name' => player.last_name,
116
+ 'overall' => player.overall,
117
+ 'position' => player.position.code
118
+ }
119
+ ]
120
+ end
121
+ end
122
+
123
+ def serialize_events(events)
124
+ events.map do |event|
125
+ {
126
+ 'type' => event.class.name.split('::').last,
127
+ 'front_office' => event.front_office.id,
128
+ 'pick' => event.pick,
129
+ 'round' => event.round,
130
+ 'round_pick' => event.round_pick
131
+ }.tap do |hash|
132
+ hash['player'] = event.player.id if event.respond_to?(:player)
133
+ end
134
+ end
135
+ end
136
+
137
+ def deserialize_front_offices(json)
138
+ (json.dig('room', 'front_offices') || []).map do |id, front_office_hash|
139
+ prioritized_positions = (front_office_hash['prioritized_positions'] || []).map do |v|
140
+ Position.new(v)
141
+ end
142
+
143
+ front_office_opts = {
144
+ id:,
145
+ name: front_office_hash['name'],
146
+ prioritized_positions:,
147
+ fuzz: front_office_hash['fuzz'],
148
+ depth: front_office_hash['depth']
149
+ }
150
+
151
+ FrontOffice.new(**front_office_opts)
152
+ end
153
+ end
154
+
155
+ def deserialize_players(json)
156
+ (json.dig('room', 'players') || []).map do |id, player_hash|
157
+ player_opts = {
158
+ id:,
159
+ first_name: player_hash['first_name'],
160
+ last_name: player_hash['last_name'],
161
+ overall: player_hash['overall'],
162
+ position: Position.new(player_hash['position'])
163
+ }
164
+
165
+ Player.new(**player_opts)
166
+ end
167
+ end
168
+
169
+ def deserialize_events(json, players, front_offices)
170
+ (json.dig('room', 'events') || []).map do |event_hash|
171
+ event_opts = event_hash.slice('pick', 'round', 'round_pick').merge(
172
+ front_office: front_offices.find { |t| t.id == event_hash['front_office'] }
173
+ )
174
+
175
+ class_constant = EVENT_CLASSES.fetch(event_hash['type'])
176
+
177
+ if [PickEvent, SimEvent].include?(class_constant)
178
+ event_opts[:player] = players.find { |p| p.id == event_hash['player'] }
179
+ end
180
+
181
+ class_constant.new(**event_opts)
182
+ end
183
+ end
184
+ end
185
+ end
186
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Basketball
4
- module Drafting
4
+ module Draft
5
5
  class Roster < Entity
6
6
  class PlayerRequiredError < StandardError; end
7
7
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Basketball
4
- module Drafting
4
+ module Draft
5
5
  class SimEvent < Event
6
6
  attr_reader_value :player
7
7
 
@@ -3,7 +3,7 @@
3
3
  require_relative 'event'
4
4
 
5
5
  module Basketball
6
- module Drafting
6
+ module Draft
7
7
  class SkipEvent < Event
8
8
  def to_s
9
9
  "skipped #{super}"
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'drafting/cli'
3
+ require_relative 'draft/cli'
4
4
 
5
5
  module Basketball
6
- module Drafting
6
+ module Draft
7
7
  class PlayerAlreadyRegisteredError < StandardError; end
8
8
  end
9
9
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Basketball
4
- module Scheduling
4
+ module Season
5
5
  class Calendar < ValueObject
6
6
  class TeamAlreadyBookedError < StandardError; end
7
7
  class InvalidGameOrderError < StandardError; end
@@ -5,28 +5,38 @@ require_relative 'preseason_game'
5
5
  require_relative 'season_game'
6
6
 
7
7
  module Basketball
8
- module Scheduling
8
+ module Season
9
9
  class CalendarSerializer
10
10
  GAME_CLASSES = {
11
11
  'PreseasonGame' => PreseasonGame,
12
12
  'SeasonGame' => SeasonGame
13
13
  }.freeze
14
14
 
15
- def deserialize(string)
16
- json = JSON.parse(string, symbolize_names: true)
15
+ def to_hash(calendar)
16
+ teams = calendar.games.flat_map(&:teams).uniq
17
17
 
18
+ {
19
+ 'year' => calendar.preseason_start_date.year,
20
+ 'teams' => serialize_teams(teams),
21
+ 'games' => serialize_games(calendar.games)
22
+ }
23
+ end
24
+
25
+ def from_hash(json)
18
26
  Calendar.new(
19
- year: json[:year].to_i,
27
+ year: json['year'].to_i,
20
28
  games: deserialize_games(json)
21
29
  )
22
30
  end
23
31
 
32
+ def deserialize(string)
33
+ json = JSON.parse(string)
34
+
35
+ from_hash(json)
36
+ end
37
+
24
38
  def serialize(calendar)
25
- {
26
- year: calendar.preseason_start_date.year,
27
- teams: serialize_teams(calendar.games.flat_map(&:teams).uniq),
28
- games: serialize_games(calendar.games)
29
- }.to_json
39
+ to_hash(calendar).to_json
30
40
  end
31
41
 
32
42
  private
@@ -34,20 +44,20 @@ module Basketball
34
44
  ## Deserialization
35
45
 
36
46
  def deserialize_games(json)
37
- teams = deserialize_teams(json[:teams])
47
+ teams = deserialize_teams(json['teams'])
38
48
 
39
- (json[:games] || []).map do |game_hash|
40
- GAME_CLASSES.fetch(game_hash[:type]).new(
41
- date: Date.parse(game_hash[:date]),
42
- home_team: teams.fetch(game_hash[:home_team]),
43
- away_team: teams.fetch(game_hash[:away_team])
49
+ (json['games'] || []).map do |game_hash|
50
+ GAME_CLASSES.fetch(game_hash['type']).new(
51
+ date: Date.parse(game_hash['date']),
52
+ home_team: teams.fetch(game_hash['home_team']),
53
+ away_team: teams.fetch(game_hash['away_team'])
44
54
  )
45
55
  end
46
56
  end
47
57
 
48
58
  def deserialize_teams(teams)
49
59
  (teams || []).to_h do |id, team_hash|
50
- team = Team.new(id:, name: team_hash[:name])
60
+ team = Team.new(id:, name: team_hash['name'])
51
61
 
52
62
  [
53
63
  team.id,
@@ -63,7 +73,7 @@ module Basketball
63
73
  [
64
74
  team.id,
65
75
  {
66
- name: team.name
76
+ 'name' => team.name
67
77
  }
68
78
  ]
69
79
  end
@@ -72,10 +82,10 @@ module Basketball
72
82
  def serialize_games(games)
73
83
  games.sort_by(&:date).map do |game|
74
84
  {
75
- type: game.class.name.split('::').last,
76
- date: game.date,
77
- home_team: game.home_team.id,
78
- away_team: game.away_team.id
85
+ 'type' => game.class.name.split('::').last,
86
+ 'date' => game.date.to_s,
87
+ 'home_team' => game.home_team.id,
88
+ 'away_team' => game.away_team.id
79
89
  }
80
90
  end
81
91
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Basketball
4
- module Scheduling
4
+ module Season
5
5
  class Conference < Entity
6
6
  DIVISIONS_SIZE = 3
7
7
 
@@ -3,12 +3,12 @@
3
3
  require_relative 'calendar'
4
4
 
5
5
  module Basketball
6
- module Scheduling
6
+ module Season
7
7
  # This is the service class responsible for actually picking out free dates ane pairing up teams to
8
8
  # play each other. This is a reasonable naive first pass at some underlying match-making algorithms
9
9
  # but could definitely use some help with the complexity/runtime/etc.
10
10
  class Coordinator
11
- MIN_PRESEASON_GAMES_PER_TEAM = 4
11
+ MIN_PRESEASON_GAMES_PER_TEAM = 3
12
12
  MAX_PRESEASON_GAMES_PER_TEAM = 6
13
13
 
14
14
  private_constant :MIN_PRESEASON_GAMES_PER_TEAM,
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Basketball
4
- module Scheduling
4
+ module Season
5
5
  class Division < Entity
6
6
  TEAMS_SIZE = 5
7
7
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Basketball
4
- module Scheduling
4
+ module Season
5
5
  class Game < ValueObject
6
6
  attr_reader_value :date, :home_team, :away_team
7
7
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Basketball
4
- module Scheduling
4
+ module Season
5
5
  class League
6
6
  class UnknownTeamError < StandardError; end
7
7
 
@@ -1,19 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Basketball
4
- module Scheduling
4
+ module Season
5
5
  class LeagueSerializer
6
- def deserialize(string)
7
- json = JSON.parse(string, symbolize_names: true)
8
- conferences = deserialize_conferences(json[:conferences])
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'])
9
14
 
10
15
  League.new(conferences:)
11
16
  end
12
17
 
18
+ def deserialize(string)
19
+ json = JSON.parse(string)
20
+
21
+ from_hash(json)
22
+ end
23
+
13
24
  def serialize(league)
14
- {
15
- conferences: serialize_conferences(league.conferences)
16
- }.to_json
25
+ to_hash(league).to_json
17
26
  end
18
27
 
19
28
  private
@@ -24,8 +33,8 @@ module Basketball
24
33
  (conferences || []).map do |conference_id, conference_hash|
25
34
  Conference.new(
26
35
  id: conference_id,
27
- name: conference_hash[:name],
28
- divisions: deserialize_divisions(conference_hash[:divisions])
36
+ name: conference_hash['name'],
37
+ divisions: deserialize_divisions(conference_hash['divisions'])
29
38
  )
30
39
  end
31
40
  end
@@ -34,8 +43,8 @@ module Basketball
34
43
  (divisions || []).map do |division_id, division_hash|
35
44
  Division.new(
36
45
  id: division_id,
37
- name: division_hash[:name],
38
- teams: deserialize_teams(division_hash[:teams])
46
+ name: division_hash['name'],
47
+ teams: deserialize_teams(division_hash['teams'])
39
48
  )
40
49
  end
41
50
  end
@@ -44,7 +53,7 @@ module Basketball
44
53
  (teams || []).map do |team_id, team_hash|
45
54
  Team.new(
46
55
  id: team_id,
47
- name: team_hash[:name]
56
+ name: team_hash['name']
48
57
  )
49
58
  end
50
59
  end
@@ -56,8 +65,8 @@ module Basketball
56
65
  [
57
66
  conference.id,
58
67
  {
59
- name: conference.name,
60
- divisions: serialize_divisions(conference.divisions)
68
+ 'name' => conference.name,
69
+ 'divisions' => serialize_divisions(conference.divisions)
61
70
  }
62
71
  ]
63
72
  end
@@ -68,8 +77,8 @@ module Basketball
68
77
  [
69
78
  division.id,
70
79
  {
71
- name: division.name,
72
- teams: serialize_teams(division.teams)
80
+ 'name' => division.name,
81
+ 'teams' => serialize_teams(division.teams)
73
82
  }
74
83
  ]
75
84
  end
@@ -80,7 +89,7 @@ module Basketball
80
89
  [
81
90
  team.id,
82
91
  {
83
- name: team.name
92
+ 'name' => team.name
84
93
  }
85
94
  ]
86
95
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Basketball
4
- module Scheduling
4
+ module Season
5
5
  class PreseasonGame < Game
6
6
  def to_s
7
7
  "#{super} (preseason)"
@@ -9,16 +9,16 @@ require_relative 'league_serializer'
9
9
  require_relative 'team'
10
10
 
11
11
  module Basketball
12
- module Scheduling
12
+ module Season
13
13
  # Examples:
14
- # exe/basketball-schedule -o tmp/league.json
15
- # exe/basketball-schedule -i tmp/league.json -o tmp/calendar.json
16
- # exe/basketball-schedule -i tmp/league.json -o tmp/calendar.json -y 2005
17
- # exe/basketball-schedule -c tmp/calendar.json
18
- # exe/basketball-schedule -c tmp/calendar.json -t C0-D0-T0
19
- # exe/basketball-schedule -c tmp/calendar.json -d 2005-02-03
20
- # exe/basketball-schedule -c tmp/calendar.json -d 2005-02-03 -t C0-D0-T0
21
- class CLI
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
22
  attr_reader :opts,
23
23
  :league_serializer,
24
24
  :calendar_serializer,
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Basketball
4
- module Scheduling
4
+ module Season
5
5
  class SeasonGame < Game
6
6
  end
7
7
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Basketball
4
- module Scheduling
4
+ module Season
5
5
  class Team < Entity
6
6
  attr_reader :name
7
7
 
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'scheduling/cli'
3
+ require_relative 'season/scheduling_cli'
4
4
 
5
5
  module Basketball
6
- module Scheduling
6
+ module Season
7
7
  class ConferenceAlreadyRegisteredError < StandardError; end
8
8
  class DivisionAlreadyRegisteredError < StandardError; end
9
9
  class TeamAlreadyRegisteredError < StandardError; end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Basketball
4
- VERSION = '0.0.6'
4
+ VERSION = '0.0.8'
5
5
  end
data/lib/basketball.rb CHANGED
@@ -12,5 +12,5 @@ require_relative 'basketball/entity'
12
12
  require_relative 'basketball/value_object'
13
13
 
14
14
  # Submodules
15
- require_relative 'basketball/drafting'
16
- require_relative 'basketball/scheduling'
15
+ require_relative 'basketball/draft'
16
+ require_relative 'basketball/season'