basketball 0.0.6 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
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'