basketball 0.0.6 → 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +20 -7
- data/Gemfile +11 -0
- data/README.md +49 -41
- data/basketball.gemspec +2 -13
- data/exe/basketball-draft +1 -1
- data/exe/{basketball-schedule → basketball-season-scheduling} +1 -1
- data/lib/basketball/{drafting → draft}/cli.rb +34 -34
- data/lib/basketball/{drafting → draft}/event.rb +1 -1
- data/lib/basketball/{drafting → draft}/front_office.rb +1 -1
- data/lib/basketball/{drafting → draft}/league.rb +1 -1
- data/lib/basketball/{drafting → draft}/pick_event.rb +1 -1
- data/lib/basketball/{drafting → draft}/player.rb +1 -1
- data/lib/basketball/{drafting → draft}/player_search.rb +1 -1
- data/lib/basketball/{drafting → draft}/position.rb +1 -1
- data/lib/basketball/{drafting/engine.rb → draft/room.rb} +2 -2
- data/lib/basketball/draft/room_serializer.rb +186 -0
- data/lib/basketball/{drafting → draft}/roster.rb +1 -1
- data/lib/basketball/{drafting → draft}/sim_event.rb +1 -1
- data/lib/basketball/{drafting → draft}/skip_event.rb +1 -1
- data/lib/basketball/{drafting.rb → draft.rb} +2 -2
- data/lib/basketball/{scheduling → season}/calendar.rb +1 -1
- data/lib/basketball/{scheduling → season}/calendar_serializer.rb +31 -21
- data/lib/basketball/{scheduling → season}/conference.rb +1 -1
- data/lib/basketball/{scheduling → season}/coordinator.rb +2 -2
- data/lib/basketball/{scheduling → season}/division.rb +1 -1
- data/lib/basketball/{scheduling → season}/game.rb +1 -1
- data/lib/basketball/{scheduling → season}/league.rb +1 -1
- data/lib/basketball/{scheduling → season}/league_serializer.rb +26 -17
- data/lib/basketball/{scheduling → season}/preseason_game.rb +1 -1
- data/lib/basketball/{scheduling/cli.rb → season/scheduling_cli.rb} +9 -9
- data/lib/basketball/{scheduling → season}/season_game.rb +1 -1
- data/lib/basketball/{scheduling → season}/team.rb +1 -1
- data/lib/basketball/{scheduling.rb → season.rb} +2 -2
- data/lib/basketball/version.rb +1 -1
- data/lib/basketball.rb +2 -2
- metadata +32 -172
- 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
|
@@ -5,28 +5,38 @@ require_relative 'preseason_game'
|
|
5
5
|
require_relative 'season_game'
|
6
6
|
|
7
7
|
module Basketball
|
8
|
-
module
|
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
|
16
|
-
|
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[
|
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[
|
47
|
+
teams = deserialize_teams(json['teams'])
|
38
48
|
|
39
|
-
(json[
|
40
|
-
GAME_CLASSES.fetch(game_hash[
|
41
|
-
date: Date.parse(game_hash[
|
42
|
-
home_team: teams.fetch(game_hash[
|
43
|
-
away_team: teams.fetch(game_hash[
|
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[
|
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
|
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
|
76
|
-
date
|
77
|
-
home_team
|
78
|
-
away_team
|
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
|
@@ -3,12 +3,12 @@
|
|
3
3
|
require_relative 'calendar'
|
4
4
|
|
5
5
|
module Basketball
|
6
|
-
module
|
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 =
|
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,19 +1,28 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Basketball
|
4
|
-
module
|
4
|
+
module Season
|
5
5
|
class LeagueSerializer
|
6
|
-
def
|
7
|
-
|
8
|
-
|
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[
|
28
|
-
divisions: deserialize_divisions(conference_hash[
|
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[
|
38
|
-
teams: deserialize_teams(division_hash[
|
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[
|
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
|
60
|
-
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
|
72
|
-
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
|
92
|
+
'name' => team.name
|
84
93
|
}
|
85
94
|
]
|
86
95
|
end
|
@@ -9,16 +9,16 @@ require_relative 'league_serializer'
|
|
9
9
|
require_relative 'team'
|
10
10
|
|
11
11
|
module Basketball
|
12
|
-
module
|
12
|
+
module Season
|
13
13
|
# Examples:
|
14
|
-
# exe/basketball-
|
15
|
-
# exe/basketball-
|
16
|
-
# exe/basketball-
|
17
|
-
# exe/basketball-
|
18
|
-
# exe/basketball-
|
19
|
-
# exe/basketball-
|
20
|
-
# exe/basketball-
|
21
|
-
class
|
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,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '
|
3
|
+
require_relative 'season/scheduling_cli'
|
4
4
|
|
5
5
|
module Basketball
|
6
|
-
module
|
6
|
+
module Season
|
7
7
|
class ConferenceAlreadyRegisteredError < StandardError; end
|
8
8
|
class DivisionAlreadyRegisteredError < StandardError; end
|
9
9
|
class TeamAlreadyRegisteredError < StandardError; end
|
data/lib/basketball/version.rb
CHANGED
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/
|
16
|
-
require_relative 'basketball/
|
15
|
+
require_relative 'basketball/draft'
|
16
|
+
require_relative 'basketball/season'
|