basketball 0.0.9 → 0.0.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +31 -21
- data/basketball.gemspec +14 -8
- data/exe/basketball +91 -0
- data/lib/basketball/app/coordinator_cli.rb +56 -72
- data/lib/basketball/app/coordinator_repository.rb +12 -88
- data/lib/basketball/app/document_repository.rb +67 -0
- data/lib/basketball/app/file_store.rb +16 -0
- data/lib/basketball/app/in_memory_store.rb +42 -0
- data/lib/basketball/app/league_repository.rb +20 -0
- data/lib/basketball/app/league_serializable.rb +99 -0
- data/lib/basketball/app/room_cli.rb +30 -26
- data/lib/basketball/app/room_repository.rb +1 -41
- data/lib/basketball/app.rb +10 -2
- data/lib/basketball/draft/pick.rb +0 -7
- data/lib/basketball/draft/room.rb +11 -13
- data/lib/basketball/entity.rb +9 -6
- data/lib/basketball/org/conference.rb +47 -0
- data/lib/basketball/org/division.rb +43 -0
- data/lib/basketball/org/has_divisions.rb +25 -0
- data/lib/basketball/org/has_players.rb +20 -0
- data/lib/basketball/org/has_teams.rb +24 -0
- data/lib/basketball/org/league.rb +59 -32
- data/lib/basketball/org.rb +12 -1
- data/lib/basketball/season/arena.rb +26 -25
- data/lib/basketball/season/calendar.rb +52 -22
- data/lib/basketball/season/coordinator.rb +25 -18
- data/lib/basketball/season/detail.rb +47 -0
- data/lib/basketball/season/exhibition.rb +1 -1
- data/lib/basketball/season/opponent.rb +6 -0
- data/lib/basketball/season/record.rb +92 -0
- data/lib/basketball/season/scheduler.rb +223 -0
- data/lib/basketball/season/standings.rb +56 -0
- data/lib/basketball/season.rb +6 -0
- data/lib/basketball/value_object.rb +6 -28
- data/lib/basketball/value_object_dsl.rb +30 -0
- data/lib/basketball/version.rb +1 -1
- metadata +22 -6
- /data/exe/{basketball-room → basketball-draft-room} +0 -0
- /data/exe/{basketball-coordinator → basketball-season-coordinator} +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 95fd9cb9bb1c51ed7a76e5640f5297ea463b412803bcfacf8c47ea8b9abc5b21
|
4
|
+
data.tar.gz: 726e72b88fd8c81a30bfb4e47742e07fd97ba88fd86d9695852b0733ee6c0e5f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b00a86aca794ea7b1ccbba172bc0fd63cfbbf4b8ff5eee2a7763f3877d8669a2b23141109fbf78461d9bd5b475e6a758381cc24c3a5d0e8b2e6dcb17f69e97bf
|
7
|
+
data.tar.gz: 5b8d75ca8cb88dc6733d1453d485a9fe8842a2e98d601f3870837bf3197718d181b77a475960202320be2d92a651ed5d741719e1d90882984d6286f629d2cd6d
|
data/README.md
CHANGED
@@ -12,30 +12,40 @@ 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-room** | Command-line executable script
|
16
|
-
**
|
17
|
-
**
|
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
|
+
**Calendar** | Stores important boundary dates (exhibition start, exhibition end, season start, and season end).
|
18
|
+
**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.
|
18
20
|
**Coordinator Repository** | Understands how to save and load Coordinator objects from JSON files on disk.
|
19
21
|
**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
|
+
**Detail** | Re-representation of a Result object but from a specific team's perspective.
|
23
|
+
**Division** | A collection of teams.
|
20
24
|
**Draft** | Bounded context (sub-module) dealing with running a round-robin player draft for teams.
|
21
25
|
**Exhibition** | Pre-season game which has no impact to team record.
|
22
26
|
**External Dependency** | Some outside system which this library or portions of this library are dependent on.
|
27
|
+
**File Store** | Implements a store that can interact with the underlying File System.
|
23
28
|
**File System** | Local operating system that the CLI will use for persistence.
|
24
29
|
**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.
|
25
30
|
**Game** | Matches up a date with two teams (home and away) to represent a coordinatord match-up.
|
26
|
-
**League** |
|
31
|
+
**League Repository** | Understands how to save and load League objects from JSON files on disk.
|
32
|
+
**League** | Describes a league in terms of structure composed of conferences, divisions, teams, and players.
|
27
33
|
**Match** | When the Coordinator needs an Arena instance to select a game winner, it will send the Arena a Match. A match is Game but also includes the active roster (players) for both teams that will participate in the game.
|
28
34
|
**Org** | Bounded context (sub-module) dealing with overall organizational structure of a sports assocation.
|
29
35
|
**Pick** | Result event emitted when a player is automatically or manually selected.
|
30
36
|
**Player** | Identitiable as a person able to be drafted. Meant to be subclassed and extended to include more intricate descriptions of a specific sport player, such as abilities, ratings, and statistics. Right now it has none of these types of traits and it meant to only serve as the base with only an overall attribute.
|
37
|
+
**Record** | Represents a team's overall record.
|
31
38
|
**Regular** | Game that counts towards regular season record.
|
32
39
|
**Result** | The outcome of a game (typically with a home and away score).
|
33
|
-
**Room CLI** | Underlying Ruby class that powers the `basketball-room` script. Basically a terminal wrapper for the Room object.
|
40
|
+
**Room CLI** | Underlying Ruby class that powers the `basketball-draft-room` script. Basically a terminal wrapper for the Room object.
|
34
41
|
**Room Repository** | Understands how to save and load Room objects from JSON files on disk.
|
35
42
|
**Room** | Main object responsible for providing an iterable interface capable of executing a draft, pick by pick.
|
43
|
+
**Scheduler** | Knows how to take a League and a year and generate a game-populated calendar.
|
36
44
|
**Scout** | Knows how to stack rank lists of players.
|
37
45
|
**Season** | Bounded context (sub-module) dealing with calendar and matchup generation.
|
38
46
|
**Skip** | Result event emitted when a front office decides to skip a round.
|
47
|
+
**Standings** | Synthesizes teams and results into team standings with win/loss records and more.
|
48
|
+
**Store** | Interface for the underlying Repository persistence layer. While a Document Repository is mainly responsible for serialization/de-serialization, the store actually knows how to read/write the data.
|
39
49
|
**Team Group** | Set of rosters that together form a cohesive league.
|
40
50
|
**Team** | Member of a league and signs players. Has games assigned and played.
|
41
51
|
|
@@ -69,61 +79,61 @@ This library ships with an example of how the Draft module could be used impleme
|
|
69
79
|
###### Generate a Fresh Draft
|
70
80
|
|
71
81
|
```zsh
|
72
|
-
basketball-room -o tmp/draft.json
|
82
|
+
basketball-draft-room -o tmp/draft.json
|
73
83
|
```
|
74
84
|
|
75
85
|
###### N Top Available Players
|
76
86
|
|
77
87
|
```zsh
|
78
|
-
basketball-room -i tmp/draft.json -t 10
|
88
|
+
basketball-draft-room -i tmp/draft.json -t 10
|
79
89
|
```
|
80
90
|
|
81
91
|
###### N Top Available Players for a Position
|
82
92
|
|
83
93
|
```zsh
|
84
|
-
basketball-room -i tmp/draft.json -t 10 -q PG
|
94
|
+
basketball-draft-room -i tmp/draft.json -t 10 -q PG
|
85
95
|
```
|
86
96
|
|
87
97
|
###### Output League
|
88
98
|
|
89
99
|
```zsh
|
90
|
-
basketball-room -i tmp/draft.json -l
|
100
|
+
basketball-draft-room -i tmp/draft.json -l
|
91
101
|
```
|
92
102
|
|
93
103
|
###### Output Event Log
|
94
104
|
|
95
105
|
```zsh
|
96
|
-
basketball-room -i tmp/draft.json -e
|
106
|
+
basketball-draft-room -i tmp/draft.json -e
|
97
107
|
```
|
98
108
|
|
99
109
|
###### Simulate N Picks
|
100
110
|
|
101
111
|
```zsh
|
102
|
-
basketball-room -i tmp/draft.json -s 10
|
112
|
+
basketball-draft-room -i tmp/draft.json -s 10
|
103
113
|
```
|
104
114
|
|
105
115
|
###### Skip N Picks
|
106
116
|
|
107
117
|
```zsh
|
108
|
-
basketball-room -i tmp/draft.json -x 10
|
118
|
+
basketball-draft-room -i tmp/draft.json -x 10
|
109
119
|
```
|
110
120
|
|
111
121
|
###### Pick Players
|
112
122
|
|
113
123
|
```zsh
|
114
|
-
basketball-room -i tmp/draft.json -p P-100,P-200,P-300
|
124
|
+
basketball-draft-room -i tmp/draft.json -p P-100,P-200,P-300
|
115
125
|
```
|
116
126
|
|
117
127
|
###### Simulate the Rest of the Draft
|
118
128
|
|
119
129
|
```zsh
|
120
|
-
basketball-room -i tmp/draft.json -a
|
130
|
+
basketball-draft-room -i tmp/draft.json -a
|
121
131
|
```
|
122
132
|
|
123
133
|
###### Help Menu
|
124
134
|
|
125
135
|
```zsh
|
126
|
-
basketball-room -h
|
136
|
+
basketball-draft-room -h
|
127
137
|
```
|
128
138
|
|
129
139
|
### Season Module
|
@@ -140,37 +150,37 @@ This library ships with an example of how the Season module could be used implem
|
|
140
150
|
###### Generate Random coordinator
|
141
151
|
|
142
152
|
```zsh
|
143
|
-
exe/basketball-coordinator -o tmp/coordinator.json
|
153
|
+
exe/basketball-season-coordinator -o tmp/coordinator.json
|
144
154
|
```
|
145
155
|
|
146
156
|
###### Sim One Day and Save to New File
|
147
157
|
|
148
158
|
```zsh
|
149
|
-
exe/basketball-coordinator -i tmp/coordinator.json -o tmp/coordinator2.json -d 1
|
159
|
+
exe/basketball-season-coordinator -i tmp/coordinator.json -o tmp/coordinator2.json -d 1
|
150
160
|
```
|
151
161
|
|
152
162
|
###### Output Event Log
|
153
163
|
|
154
164
|
```zsh
|
155
|
-
exe/basketball-coordinator -i tmp/coordinator.json -e
|
165
|
+
exe/basketball-season-coordinator -i tmp/coordinator.json -e
|
156
166
|
```
|
157
167
|
|
158
168
|
###### Sim Two Days and Save To Input File
|
159
169
|
|
160
170
|
```zsh
|
161
|
-
exe/basketball-coordinator -i tmp/coordinator.json -d 2
|
171
|
+
exe/basketball-season-coordinator -i tmp/coordinator.json -d 2
|
162
172
|
```
|
163
173
|
|
164
174
|
###### Sim Rest of Calendar
|
165
175
|
|
166
176
|
```zsh
|
167
|
-
exe/basketball-coordinator -i tmp/coordinator.json -a
|
177
|
+
exe/basketball-season-coordinator -i tmp/coordinator.json -a
|
168
178
|
```
|
169
179
|
|
170
180
|
###### Help Menu
|
171
181
|
|
172
182
|
```zsh
|
173
|
-
basketball-coordinator -h
|
183
|
+
basketball-season-coordinator -h
|
174
184
|
```
|
175
185
|
|
176
186
|
## Contributing
|
data/basketball.gemspec
CHANGED
@@ -11,14 +11,20 @@ Gem::Specification.new do |s|
|
|
11
11
|
This library is meant to serve as the domain for a basketball league/season simulator/turn-based game. It models core ideas such as: players, general managers, draft strategy, drafting, season generation, season simultation, playoff generation, playoff simulation, and more.
|
12
12
|
DESC
|
13
13
|
|
14
|
-
s.authors
|
15
|
-
s.email
|
16
|
-
s.files
|
17
|
-
s.bindir
|
18
|
-
|
19
|
-
s.
|
20
|
-
|
21
|
-
|
14
|
+
s.authors = ['Matthew Ruggio']
|
15
|
+
s.email = ['mattruggio@icloud.com']
|
16
|
+
s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(.github|bin|docs|spec)/}) }
|
17
|
+
s.bindir = 'exe'
|
18
|
+
|
19
|
+
s.executables = %w[
|
20
|
+
basketball
|
21
|
+
basketball-season-coordinator
|
22
|
+
basketball-draft-room
|
23
|
+
]
|
24
|
+
|
25
|
+
s.homepage = 'https://github.com/mattruggio/basketball'
|
26
|
+
s.license = 'MIT'
|
27
|
+
s.metadata = {
|
22
28
|
'bug_tracker_uri' => 'https://github.com/mattruggio/basketball/issues',
|
23
29
|
'changelog_uri' => 'https://github.com/mattruggio/basketball/blob/main/CHANGELOG.md',
|
24
30
|
'documentation_uri' => 'https://www.rubydoc.info/gems/basketball',
|
data/exe/basketball
ADDED
@@ -0,0 +1,91 @@
|
|
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
|
@@ -3,20 +3,27 @@
|
|
3
3
|
module Basketball
|
4
4
|
module App
|
5
5
|
# Examples:
|
6
|
-
# exe/basketball-coordinator -o tmp/coordinator.json
|
7
|
-
# exe/basketball-coordinator -i tmp/coordinator.json -o tmp/coordinator2.json -d 1
|
8
|
-
# exe/basketball-coordinator -i tmp/coordinator2.json -e
|
9
|
-
# exe/basketball-coordinator -i tmp/coordinator2.json -o tmp/coordinator2.json -d 2
|
10
|
-
# exe/basketball-coordinator -i tmp/coordinator2.json -a
|
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
11
|
#
|
12
|
-
# exe/basketball-coordinator -o tmp/coordinator.json -ae
|
12
|
+
# exe/basketball-season-coordinator -o tmp/coordinator.json -ae
|
13
13
|
class CoordinatorCLI
|
14
|
-
attr_reader :opts, :io, :
|
14
|
+
attr_reader :opts, :io, :coordinator_repository
|
15
15
|
|
16
|
-
def initialize(
|
17
|
-
|
18
|
-
|
19
|
-
|
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
|
20
27
|
|
21
28
|
if no_input? && no_output?
|
22
29
|
io.puts('Input and/or output paths are required.')
|
@@ -49,7 +56,7 @@ module Basketball
|
|
49
56
|
else
|
50
57
|
io.puts("#{coordinator.days_left} Remaining day(s) (#{coordinator.total_days} total)")
|
51
58
|
io.puts("Currently on: #{coordinator.current_date}")
|
52
|
-
io.puts("#{coordinator.exhibitions_left} Remaining
|
59
|
+
io.puts("#{coordinator.exhibitions_left} Remaining exhibition (#{coordinator.total_exhibitions} total)")
|
53
60
|
io.puts("#{coordinator.regulars_left} Remaining season (#{coordinator.total_regulars} total)")
|
54
61
|
end
|
55
62
|
end
|
@@ -92,74 +99,51 @@ module Basketball
|
|
92
99
|
def write(coordinator)
|
93
100
|
path = output? ? output : input
|
94
101
|
|
95
|
-
|
102
|
+
coordinator_repository.save(path, coordinator)
|
96
103
|
|
97
104
|
path
|
98
105
|
end
|
99
106
|
|
100
|
-
def make_league
|
101
|
-
Org::League.new
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
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
|
+
)
|
116
138
|
end
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
def make_calendar(league:)
|
121
|
-
preseason_start_date = Date.new(2000, 1, 1)
|
122
|
-
season_start_date = Date.new(2000, 1, 11)
|
123
|
-
|
124
|
-
exhibitions = make_games(
|
125
|
-
start_date: preseason_start_date,
|
126
|
-
count: 10,
|
127
|
-
league:,
|
128
|
-
game_class: Season::Exhibition
|
129
139
|
)
|
130
|
-
|
131
|
-
regulars = make_games(
|
132
|
-
start_date: season_start_date,
|
133
|
-
count: 10,
|
134
|
-
league:,
|
135
|
-
game_class: Season::Regular
|
136
|
-
)
|
137
|
-
|
138
|
-
Season::Calendar.new(
|
139
|
-
preseason_start_date:,
|
140
|
-
preseason_end_date: Date.new(2000, 1, 10),
|
141
|
-
season_start_date:,
|
142
|
-
season_end_date: Date.new(2000, 1, 20),
|
143
|
-
games: exhibitions + regulars
|
144
|
-
)
|
145
|
-
end
|
146
|
-
|
147
|
-
def make_games(start_date:, count:, league:, game_class:)
|
148
|
-
count.times.map do |i|
|
149
|
-
home_team, away_team = league.teams.sample(2)
|
150
|
-
|
151
|
-
game_class.new(
|
152
|
-
date: start_date + i,
|
153
|
-
home_opponent: Season::Opponent.new(id: home_team.id),
|
154
|
-
away_opponent: Season::Opponent.new(id: away_team.id)
|
155
|
-
)
|
156
|
-
end
|
157
140
|
end
|
158
141
|
|
159
142
|
def make_coordinator
|
160
143
|
league = make_league
|
161
|
-
|
162
|
-
calendar =
|
144
|
+
year = Time.now.year
|
145
|
+
calendar = Season::Scheduler.new.schedule(league:, year:)
|
146
|
+
current_date = calendar.games.min_by(&:date).date
|
163
147
|
|
164
148
|
Season::Coordinator.new(
|
165
149
|
calendar:,
|
@@ -173,7 +157,7 @@ module Basketball
|
|
173
157
|
if input?
|
174
158
|
io.puts("Coordinator loaded from: #{input}")
|
175
159
|
|
176
|
-
|
160
|
+
coordinator_repository.load(input)
|
177
161
|
else
|
178
162
|
io.puts('Input path was not provided, generating fresh coordinator')
|
179
163
|
|
@@ -220,7 +204,7 @@ module Basketball
|
|
220
204
|
|
221
205
|
def slop_parse(args)
|
222
206
|
Slop.parse(args) do |o|
|
223
|
-
o.banner = 'Usage: basketball-coordinator [options] ...'
|
207
|
+
o.banner = 'Usage: basketball-season-coordinator [options] ...'
|
224
208
|
|
225
209
|
input_description = <<~DESC
|
226
210
|
Path to load the Coordinator from. If omitted then a new coordinator will be created.
|
@@ -3,7 +3,9 @@
|
|
3
3
|
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
|
-
class CoordinatorRepository
|
6
|
+
class CoordinatorRepository < DocumentRepository
|
7
|
+
include LeagueSerializable
|
8
|
+
|
7
9
|
GAME_CLASSES = {
|
8
10
|
'Exhibition' => Season::Exhibition,
|
9
11
|
'Regular' => Season::Regular
|
@@ -11,46 +13,8 @@ module Basketball
|
|
11
13
|
|
12
14
|
private_constant :GAME_CLASSES
|
13
15
|
|
14
|
-
attr_reader :store
|
15
|
-
|
16
|
-
def initialize(store: FileStore.new)
|
17
|
-
super()
|
18
|
-
|
19
|
-
@store = store
|
20
|
-
|
21
|
-
freeze
|
22
|
-
end
|
23
|
-
|
24
|
-
def load(path)
|
25
|
-
contents = store.read(path)
|
26
|
-
|
27
|
-
deserialize(contents).tap do |coordinator|
|
28
|
-
coordinator.send('id=', path)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def save(path, coordinator)
|
33
|
-
contents = serialize(coordinator)
|
34
|
-
|
35
|
-
store.write(path, contents)
|
36
|
-
|
37
|
-
coordinator.send('id=', path)
|
38
|
-
|
39
|
-
coordinator
|
40
|
-
end
|
41
|
-
|
42
16
|
private
|
43
17
|
|
44
|
-
def deserialize(string)
|
45
|
-
hash = JSON.parse(string, symbolize_names: true)
|
46
|
-
|
47
|
-
from_h(hash)
|
48
|
-
end
|
49
|
-
|
50
|
-
def serialize(object)
|
51
|
-
to_h(object).to_json
|
52
|
-
end
|
53
|
-
|
54
18
|
def from_h(hash)
|
55
19
|
Season::Coordinator.new(
|
56
20
|
calendar: deserialize_calendar(hash[:calendar]),
|
@@ -62,6 +26,7 @@ module Basketball
|
|
62
26
|
|
63
27
|
def to_h(coordinator)
|
64
28
|
{
|
29
|
+
id: coordinator.id,
|
65
30
|
calendar: serialize_calendar(coordinator.calendar),
|
66
31
|
current_date: coordinator.current_date.to_s,
|
67
32
|
results: serialize_results(coordinator.results),
|
@@ -71,24 +36,16 @@ module Basketball
|
|
71
36
|
|
72
37
|
# Serialization
|
73
38
|
|
74
|
-
def serialize_player(player)
|
75
|
-
{
|
76
|
-
id: player.id,
|
77
|
-
overall: player.overall,
|
78
|
-
position: player.position&.code
|
79
|
-
}
|
80
|
-
end
|
81
|
-
|
82
39
|
def serialize_games(games)
|
83
40
|
games.map { |game| serialize_game(game) }
|
84
41
|
end
|
85
42
|
|
86
43
|
def serialize_calendar(calendar)
|
87
44
|
{
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
45
|
+
exhibition_start_date: calendar.exhibition_start_date.to_s,
|
46
|
+
exhibition_end_date: calendar.exhibition_end_date.to_s,
|
47
|
+
regular_start_date: calendar.regular_start_date.to_s,
|
48
|
+
regular_end_date: calendar.regular_end_date.to_s,
|
92
49
|
games: serialize_games(calendar.games)
|
93
50
|
}
|
94
51
|
end
|
@@ -110,19 +67,6 @@ module Basketball
|
|
110
67
|
}
|
111
68
|
end
|
112
69
|
|
113
|
-
def serialize_league(league)
|
114
|
-
{
|
115
|
-
teams: league.teams.map { |team| serialize_team(team) }
|
116
|
-
}
|
117
|
-
end
|
118
|
-
|
119
|
-
def serialize_team(team)
|
120
|
-
{
|
121
|
-
id: team.id,
|
122
|
-
players: team.players.map { |player| serialize_player(player) }
|
123
|
-
}
|
124
|
-
end
|
125
|
-
|
126
70
|
def serialize_results(results)
|
127
71
|
results.map do |result|
|
128
72
|
serialize_result(result)
|
@@ -131,32 +75,12 @@ module Basketball
|
|
131
75
|
|
132
76
|
# Deserialization
|
133
77
|
|
134
|
-
def deserialize_player(player_hash)
|
135
|
-
Org::Player.new(
|
136
|
-
id: player_hash[:id],
|
137
|
-
overall: player_hash[:overall],
|
138
|
-
position: Org::Position.new(player_hash[:position])
|
139
|
-
)
|
140
|
-
end
|
141
|
-
|
142
|
-
def deserialize_league(league_hash)
|
143
|
-
team_hashes = league_hash[:teams] || []
|
144
|
-
|
145
|
-
teams = team_hashes.map do |team_hash|
|
146
|
-
players = (team_hash[:players] || []).map { |player_hash| deserialize_player(player_hash) }
|
147
|
-
|
148
|
-
Org::Team.new(id: team_hash[:id], players:)
|
149
|
-
end
|
150
|
-
|
151
|
-
Org::League.new(teams:)
|
152
|
-
end
|
153
|
-
|
154
78
|
def deserialize_calendar(calendar_hash)
|
155
79
|
Season::Calendar.new(
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
80
|
+
exhibition_start_date: Date.parse(calendar_hash[:exhibition_start_date]),
|
81
|
+
exhibition_end_date: Date.parse(calendar_hash[:exhibition_end_date]),
|
82
|
+
regular_start_date: Date.parse(calendar_hash[:regular_start_date]),
|
83
|
+
regular_end_date: Date.parse(calendar_hash[:regular_end_date]),
|
160
84
|
games: deserialize_games(calendar_hash[:games])
|
161
85
|
)
|
162
86
|
end
|