basketball 0.0.13 → 0.0.15
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -109
- data/basketball.gemspec +1 -9
- data/lib/basketball/app/coordinator_repository.rb +2 -6
- data/lib/basketball/app/document_repository.rb +4 -0
- data/lib/basketball/app/league_repository.rb +91 -2
- data/lib/basketball/app/room_repository.rb +1 -0
- data/lib/basketball/app.rb +0 -7
- data/lib/basketball/org/league.rb +0 -2
- data/lib/basketball/season/coordinator.rb +10 -31
- data/lib/basketball/version.rb +1 -1
- data/lib/basketball.rb +0 -1
- metadata +4 -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: 27530180da34588494d04824d1fe1e9a6bfd4e0d94adc98afde3f659b070759c
|
4
|
+
data.tar.gz: 4f8e961e2e8ac1bedd5d0869d5f5f8e0e2498cb24465ef0d4505f20d4219b7fc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bbb24ef7d1f192e39dd3e0740d60210e47fcdc041e6b69ec8d9a2897a39514dca775439c8c2894f228afc6ef8ebedf2005a83ea7b8817d00247a69e5e0ae2177
|
7
|
+
data.tar.gz: 3c24e207c24e33c49baadb8bf13f8107a2bea9dc1d8bf6404601551635ef9a375f03f4b27da0b18118ef37eea55cda7d7e24b2572fd1649bc012d3a7081c9928
|
data/README.md
CHANGED
@@ -12,11 +12,8 @@ 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 CLI** | Underlying Ruby class that powers the `basketball-season-coordinator` script. Basically a terminal wrapper over the Coordinator object.
|
20
17
|
**Coordinator Repository** | Understands how to save and load Coordinator objects from JSON files on disk.
|
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.
|
@@ -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,7 +34,6 @@ 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 CLI** | Underlying Ruby class that powers the `basketball-draft-room` script. Basically a terminal wrapper for the Room object.
|
41
37
|
**Room Repository** | Understands how to save and load Room objects from JSON files on disk.
|
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.
|
@@ -72,70 +68,6 @@ The input for the main object `Basketball::Draft::Room` is an array of teams (`B
|
|
72
68
|
* **Basketball::Draft::Room#pick!(player)**: Pick an exact player for the current front office.
|
73
69
|
* **Basketball::Draft::Room#sim_rest!**: Simulate the rest of the picks.
|
74
70
|
|
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
71
|
### Season Module
|
140
72
|
|
141
73
|
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 +75,6 @@ The Season module knows how to execute a calendar of games for a League and gene
|
|
143
75
|
* **Basketball::Season::Coordinator#sim!**: Simulate the next day of games.
|
144
76
|
* **Basketball::Season::Coordinator#sim_rest!**: Simulate the rest of the games.
|
145
77
|
|
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
78
|
## Contributing
|
187
79
|
|
188
80
|
### 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
|
|
@@ -4,8 +4,6 @@ module Basketball
|
|
4
4
|
module App
|
5
5
|
# Knows how to flatten a League instance 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
|
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,3 @@ 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'
|
@@ -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
|
data/lib/basketball/version.rb
CHANGED
data/lib/basketball.rb
CHANGED
metadata
CHANGED
@@ -1,39 +1,22 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: basketball
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.15
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matthew Ruggio
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-06-
|
12
|
-
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: slop
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '4.10'
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "~>"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '4.10'
|
11
|
+
date: 2023-06-12 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
27
13
|
description: " This library is meant to serve as the domain for a basketball league/season
|
28
14
|
simulator/turn-based game. It models core ideas such as: players, general managers,
|
29
15
|
draft strategy, drafting, season generation, season simultation, playoff generation,
|
30
16
|
playoff simulation, and more.\n"
|
31
17
|
email:
|
32
18
|
- mattruggio@icloud.com
|
33
|
-
executables:
|
34
|
-
- basketball
|
35
|
-
- basketball-season-coordinator
|
36
|
-
- basketball-draft-room
|
19
|
+
executables: []
|
37
20
|
extensions: []
|
38
21
|
extra_rdoc_files: []
|
39
22
|
files:
|
@@ -49,19 +32,13 @@ files:
|
|
49
32
|
- README.md
|
50
33
|
- Rakefile
|
51
34
|
- basketball.gemspec
|
52
|
-
- exe/basketball
|
53
|
-
- exe/basketball-draft-room
|
54
|
-
- exe/basketball-season-coordinator
|
55
35
|
- lib/basketball.rb
|
56
36
|
- lib/basketball/app.rb
|
57
|
-
- lib/basketball/app/coordinator_cli.rb
|
58
37
|
- lib/basketball/app/coordinator_repository.rb
|
59
38
|
- lib/basketball/app/document_repository.rb
|
60
39
|
- lib/basketball/app/file_store.rb
|
61
40
|
- lib/basketball/app/in_memory_store.rb
|
62
41
|
- lib/basketball/app/league_repository.rb
|
63
|
-
- lib/basketball/app/league_serializable.rb
|
64
|
-
- lib/basketball/app/room_cli.rb
|
65
42
|
- lib/basketball/app/room_repository.rb
|
66
43
|
- lib/basketball/draft.rb
|
67
44
|
- lib/basketball/draft/assessment.rb
|
data/exe/basketball
DELETED
@@ -1,91 +0,0 @@
|
|
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
|
data/exe/basketball-draft-room
DELETED
@@ -1,227 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Basketball
|
4
|
-
module App
|
5
|
-
# Examples:
|
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
|
-
#
|
12
|
-
# exe/basketball-season-coordinator -o tmp/coordinator.json -ae
|
13
|
-
class CoordinatorCLI
|
14
|
-
attr_reader :opts, :io, :coordinator_repository
|
15
|
-
|
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
|
27
|
-
|
28
|
-
if no_input? && no_output?
|
29
|
-
io.puts('Input and/or output paths are required.')
|
30
|
-
|
31
|
-
exit
|
32
|
-
end
|
33
|
-
|
34
|
-
freeze
|
35
|
-
end
|
36
|
-
|
37
|
-
def invoke!
|
38
|
-
coordinator = read
|
39
|
-
|
40
|
-
execute(coordinator)
|
41
|
-
output_status(coordinator)
|
42
|
-
write(coordinator)
|
43
|
-
events(coordinator)
|
44
|
-
|
45
|
-
self
|
46
|
-
end
|
47
|
-
|
48
|
-
private
|
49
|
-
|
50
|
-
def output_status(coordinator)
|
51
|
-
io.puts
|
52
|
-
io.puts('Status')
|
53
|
-
|
54
|
-
if coordinator.done?
|
55
|
-
io.puts('Coordinator is complete!')
|
56
|
-
else
|
57
|
-
io.puts("#{coordinator.days_left} Remaining day(s) (#{coordinator.total_days} total)")
|
58
|
-
io.puts("Currently on: #{coordinator.current_date}")
|
59
|
-
io.puts("#{coordinator.exhibitions_left} Remaining exhibition (#{coordinator.total_exhibitions} total)")
|
60
|
-
io.puts("#{coordinator.regulars_left} Remaining season (#{coordinator.total_regulars} total)")
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def execute(coordinator)
|
65
|
-
event_count = 0
|
66
|
-
|
67
|
-
io.puts
|
68
|
-
io.puts('New Events')
|
69
|
-
|
70
|
-
days&.times do
|
71
|
-
coordinator.sim! do |event|
|
72
|
-
io.puts(event)
|
73
|
-
|
74
|
-
event_count += 1
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
if sim_all
|
79
|
-
coordinator.sim_rest! do |event|
|
80
|
-
io.puts(event)
|
81
|
-
|
82
|
-
event_count += 1
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
io.puts("Generated #{event_count} new event(s)")
|
87
|
-
|
88
|
-
nil
|
89
|
-
end
|
90
|
-
|
91
|
-
def days
|
92
|
-
opts[:days]
|
93
|
-
end
|
94
|
-
|
95
|
-
def sim_all
|
96
|
-
opts[:sim_all]
|
97
|
-
end
|
98
|
-
|
99
|
-
def write(coordinator)
|
100
|
-
path = output? ? output : input
|
101
|
-
|
102
|
-
coordinator_repository.save(path, coordinator)
|
103
|
-
|
104
|
-
path
|
105
|
-
end
|
106
|
-
|
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
|
-
)
|
138
|
-
end
|
139
|
-
)
|
140
|
-
end
|
141
|
-
|
142
|
-
def make_coordinator
|
143
|
-
league = make_league
|
144
|
-
year = Time.now.year
|
145
|
-
calendar = Season::Scheduler.new.schedule(league:, year:)
|
146
|
-
current_date = calendar.games.min_by(&:date).date
|
147
|
-
|
148
|
-
Season::Coordinator.new(
|
149
|
-
calendar:,
|
150
|
-
current_date:,
|
151
|
-
league:
|
152
|
-
)
|
153
|
-
end
|
154
|
-
|
155
|
-
def read
|
156
|
-
coordinator =
|
157
|
-
if input?
|
158
|
-
io.puts("Coordinator loaded from: #{input}")
|
159
|
-
|
160
|
-
coordinator_repository.load(input)
|
161
|
-
else
|
162
|
-
io.puts('Input path was not provided, generating fresh coordinator')
|
163
|
-
|
164
|
-
make_coordinator
|
165
|
-
end
|
166
|
-
|
167
|
-
io.puts("Current Date: #{coordinator.current_date}")
|
168
|
-
|
169
|
-
coordinator
|
170
|
-
end
|
171
|
-
|
172
|
-
def input
|
173
|
-
opts[:input]
|
174
|
-
end
|
175
|
-
|
176
|
-
def input?
|
177
|
-
!no_input?
|
178
|
-
end
|
179
|
-
|
180
|
-
def no_input?
|
181
|
-
input.to_s.empty?
|
182
|
-
end
|
183
|
-
|
184
|
-
def output
|
185
|
-
opts[:output]
|
186
|
-
end
|
187
|
-
|
188
|
-
def no_output?
|
189
|
-
output.to_s.empty?
|
190
|
-
end
|
191
|
-
|
192
|
-
def output?
|
193
|
-
!no_output?
|
194
|
-
end
|
195
|
-
|
196
|
-
def events(coordinator)
|
197
|
-
return unless opts[:events]
|
198
|
-
|
199
|
-
io.puts
|
200
|
-
io.puts('Event Log')
|
201
|
-
|
202
|
-
puts coordinator.results
|
203
|
-
end
|
204
|
-
|
205
|
-
def slop_parse(args)
|
206
|
-
Slop.parse(args) do |o|
|
207
|
-
o.banner = 'Usage: basketball-season-coordinator [options] ...'
|
208
|
-
|
209
|
-
input_description = <<~DESC
|
210
|
-
Path to load the Coordinator from. If omitted then a new coordinator will be created.
|
211
|
-
DESC
|
212
|
-
|
213
|
-
o.string '-i', '--input', input_description.chomp
|
214
|
-
o.string '-o', '--output', 'Path to save updated coordinator. If omitted then the input path will be used.'
|
215
|
-
o.integer '-d', '--days', 'Number of days to simulate'
|
216
|
-
o.bool '-a', '--sim-all', 'Simulate the rest of the coordinator', default: false
|
217
|
-
o.bool '-e', '--events', 'Output event log.', default: false
|
218
|
-
|
219
|
-
o.on '-h', '--help', 'Print out help, like this is doing right now.' do
|
220
|
-
io.puts(o)
|
221
|
-
exit
|
222
|
-
end
|
223
|
-
end.to_h
|
224
|
-
end
|
225
|
-
end
|
226
|
-
end
|
227
|
-
end
|
@@ -1,99 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Basketball
|
4
|
-
module App
|
5
|
-
# Provides methods to serialize/deserialize a League object
|
6
|
-
module LeagueSerializable
|
7
|
-
# Serialization
|
8
|
-
|
9
|
-
def serialize_league(league)
|
10
|
-
{
|
11
|
-
id: league.id,
|
12
|
-
conferences: serialize_conferences(league.conferences)
|
13
|
-
}
|
14
|
-
end
|
15
|
-
|
16
|
-
def serialize_conferences(conferences)
|
17
|
-
conferences.map do |conference|
|
18
|
-
{
|
19
|
-
id: conference.id,
|
20
|
-
divisions: serialize_divisions(conference.divisions)
|
21
|
-
}
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def serialize_divisions(divisions)
|
26
|
-
divisions.map do |division|
|
27
|
-
{
|
28
|
-
id: division.id,
|
29
|
-
teams: serialize_teams(division.teams)
|
30
|
-
}
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def serialize_teams(teams)
|
35
|
-
teams.map do |team|
|
36
|
-
{
|
37
|
-
id: team.id,
|
38
|
-
players: serialize_players(team.players)
|
39
|
-
}
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
def serialize_players(players)
|
44
|
-
players.map do |player|
|
45
|
-
{
|
46
|
-
id: player.id,
|
47
|
-
overall: player.overall,
|
48
|
-
position: player.position&.code
|
49
|
-
}
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
# Deserialization
|
54
|
-
|
55
|
-
def deserialize_league(league_hash)
|
56
|
-
Org::League.new(
|
57
|
-
conferences: deserialize_conferences(league_hash[:conferences])
|
58
|
-
)
|
59
|
-
end
|
60
|
-
|
61
|
-
def deserialize_conferences(conference_hashes)
|
62
|
-
(conference_hashes || []).map do |conference_hash|
|
63
|
-
Org::Conference.new(
|
64
|
-
id: conference_hash[:id],
|
65
|
-
divisions: deserialize_divisions(conference_hash[:divisions])
|
66
|
-
)
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
def deserialize_divisions(division_hashes)
|
71
|
-
(division_hashes || []).map do |division_hash|
|
72
|
-
Org::Division.new(
|
73
|
-
id: division_hash[:id],
|
74
|
-
teams: deserialize_teams(division_hash[:teams])
|
75
|
-
)
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
def deserialize_teams(team_hashes)
|
80
|
-
(team_hashes || []).map do |team_hash|
|
81
|
-
Org::Team.new(
|
82
|
-
id: team_hash[:id],
|
83
|
-
players: deserialize_players(team_hash[:players])
|
84
|
-
)
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
def deserialize_players(player_hashes)
|
89
|
-
(player_hashes || []).map do |player_hash|
|
90
|
-
Org::Player.new(
|
91
|
-
id: player_hash[:id],
|
92
|
-
overall: player_hash[:overall],
|
93
|
-
position: Org::Position.new(player_hash[:position])
|
94
|
-
)
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
@@ -1,216 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Basketball
|
4
|
-
module App
|
5
|
-
# Examples:
|
6
|
-
# exe/basketball-draft-room -o tmp/draft.json
|
7
|
-
# exe/basketball-draft-room -i tmp/draft.json -o tmp/draft-wip.json -s 26 -p P-5,P-10 -l 10
|
8
|
-
# exe/basketball-draft-room -i tmp/draft-wip.json -x 2
|
9
|
-
# exe/basketball-draft-room -i tmp/draft-wip.json -g -l 10
|
10
|
-
# exe/basketball-draft-room -i tmp/draft-wip.json -s 30 -l 10
|
11
|
-
# exe/basketball-draft-room -i tmp/draft-wip.json -ate
|
12
|
-
class RoomCLI
|
13
|
-
class PlayerNotFound < StandardError; end
|
14
|
-
|
15
|
-
attr_reader :opts,
|
16
|
-
:io,
|
17
|
-
:room_repository
|
18
|
-
|
19
|
-
def initialize(
|
20
|
-
args:,
|
21
|
-
io: $stdout,
|
22
|
-
room_repository: RoomRepository.new(FileStore.new)
|
23
|
-
)
|
24
|
-
@io = io
|
25
|
-
@opts = slop_parse(args)
|
26
|
-
@room_repository = room_repository
|
27
|
-
|
28
|
-
if opts[:input].to_s.empty? && opts[:output].to_s.empty?
|
29
|
-
io.puts('Input and/or output paths are required.')
|
30
|
-
|
31
|
-
exit
|
32
|
-
end
|
33
|
-
|
34
|
-
freeze
|
35
|
-
end
|
36
|
-
|
37
|
-
def invoke!
|
38
|
-
room = load_room
|
39
|
-
|
40
|
-
execute(room)
|
41
|
-
status(room)
|
42
|
-
write(room)
|
43
|
-
events(room)
|
44
|
-
teams(room)
|
45
|
-
query(room)
|
46
|
-
|
47
|
-
self
|
48
|
-
end
|
49
|
-
|
50
|
-
private
|
51
|
-
|
52
|
-
def status(room)
|
53
|
-
io.puts
|
54
|
-
io.puts('Status')
|
55
|
-
|
56
|
-
if room.done?
|
57
|
-
io.puts('Draft is complete!')
|
58
|
-
else
|
59
|
-
round = room.round
|
60
|
-
round_pick = room.round_pick
|
61
|
-
front_office = room.front_office
|
62
|
-
|
63
|
-
io.puts("#{room.remaining_picks} Remaining pick(s)")
|
64
|
-
io.puts("Up Next: Round #{round} pick #{round_pick} for #{front_office}")
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
def slop_parse(args)
|
69
|
-
Slop.parse(args) do |o|
|
70
|
-
o.banner = 'Usage: basketball-draft-room [options] ...'
|
71
|
-
|
72
|
-
o.string '-i', '--input', 'Path to load the Room from. If omitted then a new draft will be generated.'
|
73
|
-
o.string '-o', '--output', 'Path to write the room to (if omitted then input path will be used)'
|
74
|
-
o.integer '-s', '--simulate', 'Number of picks to simulate (default is 0).', default: 0
|
75
|
-
o.bool '-a', '--simulate-all', 'Simulate the rest of the draft', default: false
|
76
|
-
o.array '-p', '--picks', 'Comma-separated list of ordered player IDs to pick.', delimiter: ','
|
77
|
-
o.integer '-l', '--list', 'List the top rated available players (default is 0).', default: 0
|
78
|
-
o.bool '-t', '--teams', 'Output all teams and their picks', default: false
|
79
|
-
o.integer '-x', '--skip', 'Number of picks to skip (default is 0).', default: 0
|
80
|
-
o.bool '-e', '--events', 'Output event log.', default: false
|
81
|
-
|
82
|
-
o.on '-h', '--help', 'Print out help, like this is doing right now.' do
|
83
|
-
io.puts(o)
|
84
|
-
exit
|
85
|
-
end
|
86
|
-
end.to_h
|
87
|
-
end
|
88
|
-
|
89
|
-
def load_room
|
90
|
-
if opts[:input].to_s.empty?
|
91
|
-
io.puts('Input path was not provided, generating fresh front_offices and players')
|
92
|
-
|
93
|
-
generate_draft
|
94
|
-
else
|
95
|
-
io.puts("Draft loaded from: #{opts[:input]}")
|
96
|
-
|
97
|
-
read
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
def generate_draft
|
102
|
-
front_offices = 30.times.map do |i|
|
103
|
-
Draft::FrontOffice.new(
|
104
|
-
id: "T-#{i + 1}"
|
105
|
-
)
|
106
|
-
end
|
107
|
-
|
108
|
-
players = 450.times.map do |i|
|
109
|
-
Org::Player.new(
|
110
|
-
id: "P-#{i + 1}",
|
111
|
-
overall: (20..100).to_a.sample,
|
112
|
-
position: Org::Position.random
|
113
|
-
)
|
114
|
-
end
|
115
|
-
|
116
|
-
Draft::Room.new(rounds: 12, players:, front_offices:)
|
117
|
-
end
|
118
|
-
|
119
|
-
def teams(room)
|
120
|
-
return unless opts[:teams]
|
121
|
-
|
122
|
-
io.puts
|
123
|
-
io.puts(room.teams)
|
124
|
-
end
|
125
|
-
|
126
|
-
def events(room)
|
127
|
-
return unless opts[:events]
|
128
|
-
|
129
|
-
io.puts
|
130
|
-
io.puts('Event Log')
|
131
|
-
|
132
|
-
puts room.events
|
133
|
-
end
|
134
|
-
|
135
|
-
def query(room)
|
136
|
-
list = opts[:list]
|
137
|
-
|
138
|
-
return if list <= 0
|
139
|
-
|
140
|
-
players = room.undrafted_players.sort_by(&:overall).reverse.take(opts[:list])
|
141
|
-
|
142
|
-
io.puts
|
143
|
-
io.puts("Top #{list} available players")
|
144
|
-
io.puts(players)
|
145
|
-
end
|
146
|
-
|
147
|
-
def read
|
148
|
-
room_repository.load(opts[:input])
|
149
|
-
end
|
150
|
-
|
151
|
-
# rubocop:disable Metrics/AbcSize
|
152
|
-
def execute(room)
|
153
|
-
event_count = 0
|
154
|
-
|
155
|
-
io.puts
|
156
|
-
io.puts('New Events')
|
157
|
-
|
158
|
-
(opts[:picks] || []).each do |id|
|
159
|
-
break if room.done?
|
160
|
-
|
161
|
-
player = room.players.find { |p| p.id == id.to_s.upcase }
|
162
|
-
|
163
|
-
raise PlayerNotFound, "player not found by id: #{id}" unless player
|
164
|
-
|
165
|
-
event = room.pick!(player)
|
166
|
-
|
167
|
-
io.puts(event)
|
168
|
-
|
169
|
-
event_count += 1
|
170
|
-
end
|
171
|
-
|
172
|
-
opts[:skip].times do
|
173
|
-
event = room.skip!
|
174
|
-
|
175
|
-
io.puts(event)
|
176
|
-
|
177
|
-
event_count += 1
|
178
|
-
end
|
179
|
-
|
180
|
-
opts[:simulate].times do
|
181
|
-
room.sim!
|
182
|
-
|
183
|
-
event_count += 1
|
184
|
-
end
|
185
|
-
|
186
|
-
if opts[:simulate_all]
|
187
|
-
room.sim_rest! do |event|
|
188
|
-
io.puts(event)
|
189
|
-
|
190
|
-
event_count += 1
|
191
|
-
end
|
192
|
-
end
|
193
|
-
|
194
|
-
io.puts("Generated #{event_count} new event(s)")
|
195
|
-
|
196
|
-
nil
|
197
|
-
end
|
198
|
-
# rubocop:enable Metrics/AbcSize
|
199
|
-
|
200
|
-
def output_default_to_input
|
201
|
-
opts[:output].to_s.empty? ? opts[:input] : opts[:output]
|
202
|
-
end
|
203
|
-
|
204
|
-
def write(room)
|
205
|
-
output = output_default_to_input
|
206
|
-
|
207
|
-
room_repository.save(output, room)
|
208
|
-
|
209
|
-
io.puts
|
210
|
-
io.puts("Draft written to: #{output}")
|
211
|
-
|
212
|
-
nil
|
213
|
-
end
|
214
|
-
end
|
215
|
-
end
|
216
|
-
end
|