basketball 0.0.14 → 0.0.16
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.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/README.md +4 -111
- data/basketball.gemspec +1 -9
- data/lib/basketball/app/coordinator_repository.rb +2 -6
- data/lib/basketball/app/league_repository.rb +92 -3
- data/lib/basketball/app/room_repository.rb +2 -1
- data/lib/basketball/app/standings_repository.rb +69 -0
- data/lib/basketball/app.rb +1 -7
- data/lib/basketball/org/league.rb +0 -2
- data/lib/basketball/season/coordinator.rb +10 -31
- data/lib/basketball/season/record.rb +12 -6
- data/lib/basketball/season/standings.rb +16 -5
- data/lib/basketball/version.rb +1 -1
- data/lib/basketball.rb +0 -1
- metadata +5 -27
- data/exe/basketball +0 -91
- data/exe/basketball-draft-room +0 -7
- data/exe/basketball-season-coordinator +0 -7
- data/lib/basketball/app/coordinator_cli.rb +0 -227
- data/lib/basketball/app/league_serializable.rb +0 -99
- data/lib/basketball/app/room_cli.rb +0 -216
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f5be10590a625ef061cb6f7a80379710013ec3625fe215fa65bfddeda35bb1f5
|
4
|
+
data.tar.gz: 5eff4fd535bddae0bf080f2fd0be83b4cdc8da7cc28c04667a1ec7cfd84f1f55
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 41e8aac1b7270794a5557b9e94b3b23bd0f5f7169c5c38f87401eaf12e6865efd4a1b8293a8cf6b6a41813ecc476750f538abc511ae4554e94c3cc732c6a5872
|
7
|
+
data.tar.gz: fd0fcc3117888b9c475efce63e5947de6f66950b3af98af9f4c551403749ab34fc6f989d3b48964bc29180be19c3af55931e5bb4ef67377f05dd83994ab82284
|
data/.rubocop.yml
CHANGED
data/README.md
CHANGED
@@ -12,12 +12,9 @@ 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-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
15
|
**Calendar** | Stores important boundary dates (exhibition start, exhibition end, season start, and season end).
|
18
16
|
**Conference** | A collection of Divisions.
|
19
|
-
**Coordinator
|
20
|
-
**Coordinator Repository** | Understands how to save and load Coordinator objects from JSON files on disk.
|
17
|
+
**Coordinator Repository** | Understands how to save and load Coordinator objects.
|
21
18
|
**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
19
|
**Detail** | Re-representation of a Result object but from a specific team's perspective.
|
23
20
|
**Division** | A collection of teams.
|
@@ -25,7 +22,7 @@ Element | Description
|
|
25
22
|
**Exhibition** | Pre-season game which has no impact to team record.
|
26
23
|
**External Dependency** | Some outside system which this library or portions of this library are dependent on.
|
27
24
|
**File Store** | Implements a store that can interact with the underlying File System.
|
28
|
-
**File System** | Local operating system that
|
25
|
+
**File System** | Local operating system that repositories can use as their underlying persistence layer.
|
29
26
|
**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.
|
30
27
|
**Game** | Matches up a date with two teams (home and away) to represent a coordinatord match-up.
|
31
28
|
**League Repository** | Understands how to save and load League objects from JSON files on disk.
|
@@ -37,14 +34,14 @@ Element | Description
|
|
37
34
|
**Record** | Represents a team's overall record.
|
38
35
|
**Regular** | Game that counts towards regular season record.
|
39
36
|
**Result** | The outcome of a game (typically with a home and away score).
|
40
|
-
**Room
|
41
|
-
**Room Repository** | Understands how to save and load Room objects from JSON files on disk.
|
37
|
+
**Room Repository** | Understands how to save and load Room objects.
|
42
38
|
**Room** | Main object responsible for providing an iterable interface capable of executing a draft, pick by pick.
|
43
39
|
**Scheduler** | Knows how to take a League and a year and generate a game-populated calendar.
|
44
40
|
**Scout** | Knows how to stack rank lists of players.
|
45
41
|
**Season** | Bounded context (sub-module) dealing with calendar and matchup generation.
|
46
42
|
**Skip** | Result event emitted when a front office decides to skip a round.
|
47
43
|
**Standings** | Synthesizes teams and results into team standings with win/loss records and more.
|
44
|
+
**Standings Repository** | Understands how to save and load Standings objects.
|
48
45
|
**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.
|
49
46
|
**Team Group** | Set of rosters that together form a cohesive league.
|
50
47
|
**Team** | Member of a league and signs players. Has games assigned and played.
|
@@ -72,70 +69,6 @@ The input for the main object `Basketball::Draft::Room` is an array of teams (`B
|
|
72
69
|
* **Basketball::Draft::Room#pick!(player)**: Pick an exact player for the current front office.
|
73
70
|
* **Basketball::Draft::Room#sim_rest!**: Simulate the rest of the picks.
|
74
71
|
|
75
|
-
#### Command Line Interface
|
76
|
-
|
77
|
-
This library ships with an example of how the Draft module could be used implemented as a command-line executable script. The script is file-based and will de-serialize into a Room object, execute operations, then serialize and write it back to disk.
|
78
|
-
|
79
|
-
###### Generate a Fresh Draft
|
80
|
-
|
81
|
-
```zsh
|
82
|
-
basketball-draft-room -o tmp/draft.json
|
83
|
-
```
|
84
|
-
|
85
|
-
###### N Top Available Players
|
86
|
-
|
87
|
-
```zsh
|
88
|
-
basketball-draft-room -i tmp/draft.json -t 10
|
89
|
-
```
|
90
|
-
|
91
|
-
###### N Top Available Players for a Position
|
92
|
-
|
93
|
-
```zsh
|
94
|
-
basketball-draft-room -i tmp/draft.json -t 10 -q PG
|
95
|
-
```
|
96
|
-
|
97
|
-
###### Output League
|
98
|
-
|
99
|
-
```zsh
|
100
|
-
basketball-draft-room -i tmp/draft.json -l
|
101
|
-
```
|
102
|
-
|
103
|
-
###### Output Event Log
|
104
|
-
|
105
|
-
```zsh
|
106
|
-
basketball-draft-room -i tmp/draft.json -e
|
107
|
-
```
|
108
|
-
|
109
|
-
###### Simulate N Picks
|
110
|
-
|
111
|
-
```zsh
|
112
|
-
basketball-draft-room -i tmp/draft.json -s 10
|
113
|
-
```
|
114
|
-
|
115
|
-
###### Skip N Picks
|
116
|
-
|
117
|
-
```zsh
|
118
|
-
basketball-draft-room -i tmp/draft.json -x 10
|
119
|
-
```
|
120
|
-
|
121
|
-
###### Pick Players
|
122
|
-
|
123
|
-
```zsh
|
124
|
-
basketball-draft-room -i tmp/draft.json -p P-100,P-200,P-300
|
125
|
-
```
|
126
|
-
|
127
|
-
###### Simulate the Rest of the Draft
|
128
|
-
|
129
|
-
```zsh
|
130
|
-
basketball-draft-room -i tmp/draft.json -a
|
131
|
-
```
|
132
|
-
|
133
|
-
###### Help Menu
|
134
|
-
|
135
|
-
```zsh
|
136
|
-
basketball-draft-room -h
|
137
|
-
```
|
138
|
-
|
139
72
|
### Season Module
|
140
73
|
|
141
74
|
The Season module knows how to execute a calendar of games for a League and generate results. The main object is the `Basketball::Season::Coordinator` class. Once instantiated there are four main methods:
|
@@ -143,46 +76,6 @@ The Season module knows how to execute a calendar of games for a League and gene
|
|
143
76
|
* **Basketball::Season::Coordinator#sim!**: Simulate the next day of games.
|
144
77
|
* **Basketball::Season::Coordinator#sim_rest!**: Simulate the rest of the games.
|
145
78
|
|
146
|
-
#### Command Line Interface
|
147
|
-
|
148
|
-
This library ships with an example of how the Season module could be used implemented as a command-line executable script. The script is file-based and will de-serialize into a Coordinator object, execute operations, then serialize and write it back to disk.
|
149
|
-
|
150
|
-
###### Generate Random coordinator
|
151
|
-
|
152
|
-
```zsh
|
153
|
-
exe/basketball-season-coordinator -o tmp/coordinator.json
|
154
|
-
```
|
155
|
-
|
156
|
-
###### Sim One Day and Save to New File
|
157
|
-
|
158
|
-
```zsh
|
159
|
-
exe/basketball-season-coordinator -i tmp/coordinator.json -o tmp/coordinator2.json -d 1
|
160
|
-
```
|
161
|
-
|
162
|
-
###### Output Event Log
|
163
|
-
|
164
|
-
```zsh
|
165
|
-
exe/basketball-season-coordinator -i tmp/coordinator.json -e
|
166
|
-
```
|
167
|
-
|
168
|
-
###### Sim Two Days and Save To Input File
|
169
|
-
|
170
|
-
```zsh
|
171
|
-
exe/basketball-season-coordinator -i tmp/coordinator.json -d 2
|
172
|
-
```
|
173
|
-
|
174
|
-
###### Sim Rest of Calendar
|
175
|
-
|
176
|
-
```zsh
|
177
|
-
exe/basketball-season-coordinator -i tmp/coordinator.json -a
|
178
|
-
```
|
179
|
-
|
180
|
-
###### Help Menu
|
181
|
-
|
182
|
-
```zsh
|
183
|
-
basketball-season-coordinator -h
|
184
|
-
```
|
185
|
-
|
186
79
|
## Contributing
|
187
80
|
|
188
81
|
### Development Environment Configuration
|
data/basketball.gemspec
CHANGED
@@ -15,13 +15,7 @@ Gem::Specification.new do |s|
|
|
15
15
|
s.email = ['mattruggio@icloud.com']
|
16
16
|
s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(.github|bin|docs|spec)/}) }
|
17
17
|
s.bindir = 'exe'
|
18
|
-
|
19
|
-
s.executables = %w[
|
20
|
-
basketball
|
21
|
-
basketball-season-coordinator
|
22
|
-
basketball-draft-room
|
23
|
-
]
|
24
|
-
|
18
|
+
s.executables = %w[]
|
25
19
|
s.homepage = 'https://github.com/mattruggio/basketball'
|
26
20
|
s.license = 'MIT'
|
27
21
|
s.metadata = {
|
@@ -34,6 +28,4 @@ Gem::Specification.new do |s|
|
|
34
28
|
}
|
35
29
|
|
36
30
|
s.required_ruby_version = '>= 3.2.1'
|
37
|
-
|
38
|
-
s.add_dependency('slop', '~>4.10')
|
39
31
|
end
|
@@ -4,8 +4,6 @@ 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
6
|
class CoordinatorRepository < DocumentRepository
|
7
|
-
include LeagueSerializable
|
8
|
-
|
9
7
|
GAME_CLASSES = {
|
10
8
|
'Exhibition' => Season::Exhibition,
|
11
9
|
'Regular' => Season::Regular
|
@@ -19,8 +17,7 @@ module Basketball
|
|
19
17
|
Season::Coordinator.new(
|
20
18
|
calendar: deserialize_calendar(hash[:calendar]),
|
21
19
|
current_date: Date.parse(hash[:current_date]),
|
22
|
-
results: deserialize_results(hash[:results])
|
23
|
-
league: deserialize_league(hash[:league])
|
20
|
+
results: deserialize_results(hash[:results])
|
24
21
|
)
|
25
22
|
end
|
26
23
|
|
@@ -29,8 +26,7 @@ module Basketball
|
|
29
26
|
id: coordinator.id,
|
30
27
|
calendar: serialize_calendar(coordinator.calendar),
|
31
28
|
current_date: coordinator.current_date.to_s,
|
32
|
-
results: serialize_results(coordinator.results)
|
33
|
-
league: serialize_league(coordinator.league)
|
29
|
+
results: serialize_results(coordinator.results)
|
34
30
|
}
|
35
31
|
end
|
36
32
|
|
@@ -2,10 +2,8 @@
|
|
2
2
|
|
3
3
|
module Basketball
|
4
4
|
module App
|
5
|
-
# Knows how to flatten a League
|
5
|
+
# Knows how to flatten a League instances and rehydrate one from JSON and/or a Ruby hash.
|
6
6
|
class LeagueRepository < DocumentRepository
|
7
|
-
include LeagueSerializable
|
8
|
-
|
9
7
|
private
|
10
8
|
|
11
9
|
def from_h(hash)
|
@@ -15,6 +13,97 @@ module Basketball
|
|
15
13
|
def to_h(league)
|
16
14
|
serialize_league(league)
|
17
15
|
end
|
16
|
+
|
17
|
+
# Serialization
|
18
|
+
|
19
|
+
def serialize_league(league)
|
20
|
+
{
|
21
|
+
id: league.id,
|
22
|
+
conferences: serialize_conferences(league.conferences)
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
def serialize_conferences(conferences)
|
27
|
+
conferences.map do |conference|
|
28
|
+
{
|
29
|
+
id: conference.id,
|
30
|
+
divisions: serialize_divisions(conference.divisions)
|
31
|
+
}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def serialize_divisions(divisions)
|
36
|
+
divisions.map do |division|
|
37
|
+
{
|
38
|
+
id: division.id,
|
39
|
+
teams: serialize_teams(division.teams)
|
40
|
+
}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def serialize_teams(teams)
|
45
|
+
teams.map do |team|
|
46
|
+
{
|
47
|
+
id: team.id,
|
48
|
+
players: serialize_players(team.players)
|
49
|
+
}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def serialize_players(players)
|
54
|
+
players.map do |player|
|
55
|
+
{
|
56
|
+
id: player.id,
|
57
|
+
overall: player.overall,
|
58
|
+
position: player.position&.code
|
59
|
+
}
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Deserialization
|
64
|
+
|
65
|
+
def deserialize_league(league_hash)
|
66
|
+
Org::League.new(
|
67
|
+
conferences: deserialize_conferences(league_hash[:conferences])
|
68
|
+
)
|
69
|
+
end
|
70
|
+
|
71
|
+
def deserialize_conferences(conference_hashes)
|
72
|
+
(conference_hashes || []).map do |conference_hash|
|
73
|
+
Org::Conference.new(
|
74
|
+
id: conference_hash[:id],
|
75
|
+
divisions: deserialize_divisions(conference_hash[:divisions])
|
76
|
+
)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def deserialize_divisions(division_hashes)
|
81
|
+
(division_hashes || []).map do |division_hash|
|
82
|
+
Org::Division.new(
|
83
|
+
id: division_hash[:id],
|
84
|
+
teams: deserialize_teams(division_hash[:teams])
|
85
|
+
)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def deserialize_teams(team_hashes)
|
90
|
+
(team_hashes || []).map do |team_hash|
|
91
|
+
Org::Team.new(
|
92
|
+
id: team_hash[:id],
|
93
|
+
players: deserialize_players(team_hash[:players])
|
94
|
+
)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def deserialize_players(player_hashes)
|
99
|
+
(player_hashes || []).map do |player_hash|
|
100
|
+
Org::Player.new(
|
101
|
+
id: player_hash[:id],
|
102
|
+
overall: player_hash[:overall],
|
103
|
+
position: Org::Position.new(player_hash[:position])
|
104
|
+
)
|
105
|
+
end
|
106
|
+
end
|
18
107
|
end
|
19
108
|
end
|
20
109
|
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module Basketball
|
4
4
|
module App
|
5
|
-
# Can load and save Room objects
|
5
|
+
# Can load and save Room objects as JSON Documents.
|
6
6
|
class RoomRepository < DocumentRepository
|
7
7
|
PICK_EVENT = 'Pick'
|
8
8
|
SKIP_EVENT = 'Skip'
|
@@ -26,6 +26,7 @@ module Basketball
|
|
26
26
|
|
27
27
|
def to_h(room)
|
28
28
|
{
|
29
|
+
id: room.id,
|
29
30
|
rounds: room.rounds,
|
30
31
|
front_offices: room.front_offices.map { |fo| serialize_front_office(fo) },
|
31
32
|
players: room.players.map { |p| serialize_player(p) },
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Basketball
|
4
|
+
module App
|
5
|
+
# Can load and save Standings objects as JSON Documents.
|
6
|
+
class StandingsRepository < DocumentRepository
|
7
|
+
private
|
8
|
+
|
9
|
+
def from_h(hash)
|
10
|
+
Season::Standings.new(
|
11
|
+
records: deserialize_records(hash[:records])
|
12
|
+
)
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_h(standings)
|
16
|
+
{
|
17
|
+
id: standings.id,
|
18
|
+
records: serialize_records(standings.records)
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
# Serialization
|
23
|
+
|
24
|
+
def serialize_records(records)
|
25
|
+
(records || []).map do |record|
|
26
|
+
{
|
27
|
+
id: record.id,
|
28
|
+
details: serialize_details(record.details)
|
29
|
+
}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def serialize_details(details)
|
34
|
+
(details || []).map do |detail|
|
35
|
+
{
|
36
|
+
date: detail.date.to_s,
|
37
|
+
home: detail.home,
|
38
|
+
opponent: detail.opponent.id,
|
39
|
+
opponent_score: detail.opponent_score,
|
40
|
+
score: detail.score
|
41
|
+
}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Deserialization
|
46
|
+
|
47
|
+
def deserialize_records(record_hashes)
|
48
|
+
(record_hashes || []).map do |record_hash|
|
49
|
+
Season::Record.new(
|
50
|
+
id: record_hash[:id],
|
51
|
+
details: deserialize_details(record_hash[:details])
|
52
|
+
)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def deserialize_details(detail_hashes)
|
57
|
+
(detail_hashes || []).map do |detail_hash|
|
58
|
+
Season::Detail.new(
|
59
|
+
date: Date.parse(detail_hash[:date]),
|
60
|
+
home: detail_hash[:home],
|
61
|
+
opponent: Season::Opponent.new(id: detail_hash[:opponent]),
|
62
|
+
opponent_score: detail_hash[:opponent_score].to_i,
|
63
|
+
score: detail_hash[:score].to_i
|
64
|
+
)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/lib/basketball/app.rb
CHANGED
@@ -4,9 +4,6 @@
|
|
4
4
|
require_relative 'app/file_store'
|
5
5
|
require_relative 'app/in_memory_store'
|
6
6
|
|
7
|
-
# Serialization
|
8
|
-
require_relative 'app/league_serializable'
|
9
|
-
|
10
7
|
# Repositories / Common
|
11
8
|
require_relative 'app/document_repository'
|
12
9
|
|
@@ -14,7 +11,4 @@ require_relative 'app/document_repository'
|
|
14
11
|
require_relative 'app/coordinator_repository'
|
15
12
|
require_relative 'app/league_repository'
|
16
13
|
require_relative 'app/room_repository'
|
17
|
-
|
18
|
-
# Controllers
|
19
|
-
require_relative 'app/coordinator_cli'
|
20
|
-
require_relative 'app/room_cli'
|
14
|
+
require_relative 'app/standings_repository'
|
@@ -11,14 +11,12 @@ module Basketball
|
|
11
11
|
class OutOfBoundsError < StandardError; end
|
12
12
|
class PlayedGamesError < StandardError; end
|
13
13
|
class UnknownGameError < StandardError; end
|
14
|
-
class UnknownTeamError < StandardError; end
|
15
14
|
class UnplayedGamesError < StandardError; end
|
16
15
|
|
17
16
|
attr_reader :calendar,
|
18
17
|
:current_date,
|
19
18
|
:arena,
|
20
|
-
:results
|
21
|
-
:league
|
19
|
+
:results
|
22
20
|
|
23
21
|
def_delegators :calendar,
|
24
22
|
:exhibition_start_date,
|
@@ -30,37 +28,29 @@ module Basketball
|
|
30
28
|
:regulars_for,
|
31
29
|
:games_for
|
32
30
|
|
33
|
-
def initialize(
|
34
|
-
calendar:,
|
35
|
-
current_date:,
|
36
|
-
results: [],
|
37
|
-
league: Org::League.new
|
38
|
-
)
|
31
|
+
def initialize(calendar:, current_date:, results: [])
|
39
32
|
super()
|
40
33
|
|
41
34
|
raise ArgumentError, 'calendar is required' unless calendar
|
42
35
|
raise ArgumentError, 'current_date is required' if current_date.to_s.empty?
|
43
|
-
raise ArgumentError, 'league is required' unless league
|
44
36
|
|
45
37
|
@calendar = calendar
|
46
38
|
@current_date = current_date
|
47
39
|
@arena = Arena.new
|
48
40
|
@results = []
|
49
|
-
@league = league
|
50
41
|
|
51
42
|
results.each { |result| replay!(result) }
|
52
43
|
|
53
44
|
assert_current_date
|
54
45
|
assert_all_past_dates_are_played
|
55
46
|
assert_all_future_dates_arent_played
|
56
|
-
assert_all_known_teams
|
57
47
|
end
|
58
48
|
|
59
|
-
def sim_rest!(&)
|
49
|
+
def sim_rest!(league, &)
|
60
50
|
events = []
|
61
51
|
|
62
52
|
while not_done?
|
63
|
-
new_events = sim!(&)
|
53
|
+
new_events = sim!(league, &)
|
64
54
|
|
65
55
|
events += new_events
|
66
56
|
end
|
@@ -78,15 +68,17 @@ module Basketball
|
|
78
68
|
raise OutOfBoundsError, "current date #{current_date} should be on or after #{exhibition_start_date}"
|
79
69
|
end
|
80
70
|
|
81
|
-
def sim!
|
71
|
+
def sim!(league)
|
72
|
+
raise ArgumentError, 'league is required' unless league
|
73
|
+
|
82
74
|
return [] if done?
|
83
75
|
|
84
76
|
events = []
|
85
77
|
games = games_for(date: current_date)
|
86
78
|
|
87
79
|
games.each do |game|
|
88
|
-
home_players = opponent_team(game.home_opponent).players
|
89
|
-
away_players = opponent_team(game.away_opponent).players
|
80
|
+
home_players = opponent_team(league, game.home_opponent).players
|
81
|
+
away_players = opponent_team(league, game.away_opponent).players
|
90
82
|
matchup = Matchup.new(game:, home_players:, away_players:)
|
91
83
|
event = arena.play(matchup)
|
92
84
|
|
@@ -140,7 +132,6 @@ module Basketball
|
|
140
132
|
|
141
133
|
def add!(game)
|
142
134
|
assert_today_or_in_future(game)
|
143
|
-
assert_known_teams(game)
|
144
135
|
|
145
136
|
calendar.add!(game)
|
146
137
|
|
@@ -165,7 +156,7 @@ module Basketball
|
|
165
156
|
|
166
157
|
attr_writer :arena
|
167
158
|
|
168
|
-
def opponent_team(opponent)
|
159
|
+
def opponent_team(league, opponent)
|
169
160
|
league.teams.find { |t| t == opponent }
|
170
161
|
end
|
171
162
|
|
@@ -191,14 +182,6 @@ module Basketball
|
|
191
182
|
raise OutOfBoundsError, "#{game.date} is on or before the current date (#{current_date})"
|
192
183
|
end
|
193
184
|
|
194
|
-
def assert_known_teams(game)
|
195
|
-
raise UnknownTeamError, "unknown opponent: #{game.home_opponent}" unless league.team?(game.home_opponent)
|
196
|
-
|
197
|
-
return if league.team?(game.away_opponent)
|
198
|
-
|
199
|
-
raise UnknownTeamError, "unknown opponent: #{game.away_opponent}"
|
200
|
-
end
|
201
|
-
|
202
185
|
def assert_all_past_dates_are_played
|
203
186
|
games_that_should_be_played = games.select { |game| game.date < current_date }
|
204
187
|
|
@@ -237,10 +220,6 @@ module Basketball
|
|
237
220
|
|
238
221
|
result
|
239
222
|
end
|
240
|
-
|
241
|
-
def assert_all_known_teams
|
242
|
-
calendar.games.each { |game| assert_known_teams(game) }
|
243
|
-
end
|
244
223
|
end
|
245
224
|
end
|
246
225
|
end
|
@@ -8,7 +8,7 @@ module Basketball
|
|
8
8
|
class DetailAlreadyAddedError < StandardError; end
|
9
9
|
class OpponentNotFoundError < StandardError; end
|
10
10
|
|
11
|
-
def initialize(id:)
|
11
|
+
def initialize(id:, details: [])
|
12
12
|
super(id)
|
13
13
|
|
14
14
|
@details_by_date = {}
|
@@ -53,9 +53,15 @@ module Basketball
|
|
53
53
|
end
|
54
54
|
|
55
55
|
def win_percentage
|
56
|
+
return 0 unless game_count.positive?
|
57
|
+
|
56
58
|
(win_count.to_f / game_count).round(3)
|
57
59
|
end
|
58
60
|
|
61
|
+
def win_percentage_display
|
62
|
+
format('%.3f', win_percentage)
|
63
|
+
end
|
64
|
+
|
59
65
|
def game_count
|
60
66
|
details.length
|
61
67
|
end
|
@@ -69,17 +75,13 @@ module Basketball
|
|
69
75
|
end
|
70
76
|
|
71
77
|
def to_s
|
72
|
-
"[#{super}] #{win_count}-#{loss_count} (#{
|
78
|
+
"[#{super}] #{win_count}-#{loss_count} (#{win_percentage_display})"
|
73
79
|
end
|
74
80
|
|
75
81
|
def <=>(other)
|
76
82
|
[win_count, win_percentage] <=> [other.win_count, other.win_percentage]
|
77
83
|
end
|
78
84
|
|
79
|
-
private
|
80
|
-
|
81
|
-
attr_reader :details_by_date
|
82
|
-
|
83
85
|
def add!(detail)
|
84
86
|
raise DetailAlreadyAddedError, "#{detail} already added for date" if detail_for(detail.date)
|
85
87
|
|
@@ -87,6 +89,10 @@ module Basketball
|
|
87
89
|
|
88
90
|
self
|
89
91
|
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
attr_reader :details_by_date
|
90
96
|
end
|
91
97
|
end
|
92
98
|
end
|
@@ -3,18 +3,29 @@
|
|
3
3
|
module Basketball
|
4
4
|
module Season
|
5
5
|
# Represents a League with each team's win/loss details.
|
6
|
-
class Standings
|
6
|
+
class Standings < Entity
|
7
7
|
class TeamAlreadyRegisteredError < StandardError; end
|
8
8
|
class TeamNotRegisteredError < StandardError; end
|
9
9
|
|
10
|
-
def initialize
|
10
|
+
def initialize(records: [])
|
11
|
+
super()
|
12
|
+
|
11
13
|
@records_by_id = {}
|
14
|
+
|
15
|
+
records.each { |record| add!(record) }
|
12
16
|
end
|
13
17
|
|
14
|
-
def
|
15
|
-
raise
|
18
|
+
def add!(record)
|
19
|
+
raise ArgumentError, 'record is required' unless record
|
20
|
+
raise TeamAlreadyRegisteredError, "#{team} already registered!" if team?(record)
|
21
|
+
|
22
|
+
records_by_id[record.id] = record
|
16
23
|
|
17
|
-
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
def register!(team)
|
28
|
+
add!(Record.new(id: team.id))
|
18
29
|
|
19
30
|
self
|
20
31
|
end
|
data/lib/basketball/version.rb
CHANGED