basketball 0.0.8 → 0.0.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +9 -19
- data/CHANGELOG.md +1 -39
- data/README.md +75 -93
- data/basketball.gemspec +3 -6
- data/exe/{basketball-season-scheduling → basketball-coordinator} +1 -1
- data/exe/{basketball-draft → basketball-room} +1 -1
- data/lib/basketball/app/coordinator_cli.rb +250 -0
- data/lib/basketball/app/coordinator_repository.rb +114 -0
- data/lib/basketball/app/document_repository.rb +67 -0
- data/lib/basketball/app/file_store.rb +38 -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 +54 -0
- data/lib/basketball/{draft/cli.rb → app/room_cli.rb} +74 -80
- data/lib/basketball/app/room_repository.rb +149 -0
- data/lib/basketball/app.rb +20 -0
- data/lib/basketball/draft/assessment.rb +31 -0
- data/lib/basketball/draft/event.rb +3 -2
- data/lib/basketball/draft/front_office.rb +35 -28
- data/lib/basketball/draft/{pick_event.rb → pick.rb} +13 -6
- data/lib/basketball/draft/room.rb +119 -119
- data/lib/basketball/draft/{player_search.rb → scout.rb} +4 -9
- data/lib/basketball/draft/skip.rb +12 -0
- data/lib/basketball/draft.rb +13 -6
- data/lib/basketball/entity.rb +19 -10
- data/lib/basketball/org/league.rb +68 -0
- data/lib/basketball/org/player.rb +26 -0
- data/lib/basketball/{draft → org}/position.rb +3 -2
- data/lib/basketball/org/team.rb +38 -0
- data/lib/basketball/org.rb +12 -0
- data/lib/basketball/season/arena.rb +113 -0
- data/lib/basketball/season/calendar.rb +41 -72
- data/lib/basketball/season/coordinator.rb +186 -128
- data/lib/basketball/season/{preseason_game.rb → exhibition.rb} +2 -1
- data/lib/basketball/season/game.rb +15 -10
- data/lib/basketball/season/matchup.rb +27 -0
- data/lib/basketball/season/opponent.rb +15 -0
- data/lib/basketball/season/{season_game.rb → regular.rb} +2 -1
- data/lib/basketball/season/result.rb +37 -0
- data/lib/basketball/season.rb +12 -13
- data/lib/basketball/value_object.rb +8 -27
- data/lib/basketball/value_object_dsl.rb +30 -0
- data/lib/basketball/version.rb +1 -1
- data/lib/basketball.rb +9 -4
- metadata +37 -44
- data/lib/basketball/draft/league.rb +0 -70
- data/lib/basketball/draft/player.rb +0 -43
- data/lib/basketball/draft/room_serializer.rb +0 -186
- data/lib/basketball/draft/roster.rb +0 -37
- data/lib/basketball/draft/sim_event.rb +0 -23
- data/lib/basketball/draft/skip_event.rb +0 -13
- data/lib/basketball/season/calendar_serializer.rb +0 -94
- data/lib/basketball/season/conference.rb +0 -57
- data/lib/basketball/season/division.rb +0 -43
- data/lib/basketball/season/league.rb +0 -114
- data/lib/basketball/season/league_serializer.rb +0 -99
- data/lib/basketball/season/scheduling_cli.rb +0 -198
- data/lib/basketball/season/team.rb +0 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dca22b3ec7251a012db2afed77f3bda87bd8b59bb59e6084fff80096efcb802a
|
4
|
+
data.tar.gz: 55973e68a7aece1571d926f21e5866e045322e1009b9a5139a959d5059e1b150
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: af6b695d1ad8bd581822ffe6fe61421c5839a51e880f0d143060656a8e916e11ff8ae4ae5293e9babb2d8f45646b643d30b7753c03141f8d0c321b4010eca14d
|
7
|
+
data.tar.gz: d0addd9247f2a3b945874d628eec04e44cb826db2578d493bd99cc242beb8e78041084bf3fbffa53b6a00f2a2b788abb8540894e1caf9db5b0031a01357d5bc1
|
data/.rubocop.yml
CHANGED
@@ -6,36 +6,26 @@ AllCops:
|
|
6
6
|
NewCops: enable
|
7
7
|
Exclude:
|
8
8
|
- bin/*
|
9
|
-
- lib/basketball/draft/cli.rb
|
10
9
|
- tmp/**/*
|
11
10
|
- vendor/**/*
|
12
11
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
Metrics/MethodLength:
|
17
|
-
Max: 35
|
12
|
+
RSpec/MultipleMemoizedHelpers:
|
13
|
+
Max: 30
|
18
14
|
|
19
15
|
RSpec/ExampleLength:
|
20
|
-
Max:
|
16
|
+
Max: 25
|
21
17
|
|
22
|
-
RSpec/
|
23
|
-
Max:
|
18
|
+
RSpec/NestedGroups:
|
19
|
+
Max: 5
|
24
20
|
|
25
21
|
Metrics/ParameterLists:
|
26
22
|
Max: 6
|
27
23
|
|
28
|
-
Metrics/AbcSize:
|
29
|
-
Max: 25
|
30
|
-
|
31
24
|
Metrics/ClassLength:
|
32
|
-
Max:
|
25
|
+
Max: 300
|
33
26
|
|
34
|
-
Metrics/
|
27
|
+
Metrics/MethodLength:
|
35
28
|
Max: 30
|
36
29
|
|
37
|
-
Metrics/
|
38
|
-
Max:
|
39
|
-
|
40
|
-
Metrics/PerceivedComplexity:
|
41
|
-
Max: 9
|
30
|
+
Metrics/AbcSize:
|
31
|
+
Max: 25
|
data/CHANGELOG.md
CHANGED
@@ -1,39 +1 @@
|
|
1
|
-
|
2
|
-
#### 0.0.8 - May 15th, 2023
|
3
|
-
|
4
|
-
* Renamed Drafting to Draft to match bounded context word form.
|
5
|
-
* Renamed Drafting::Engine to Draft::Room for better language.
|
6
|
-
* Renamed Scheduling to Season to include more things other than scheduling.
|
7
|
-
* Issued README warning for 0.0.X releases not having stable APIs.
|
8
|
-
|
9
|
-
#### 0.0.7 - May 14th, 2023
|
10
|
-
|
11
|
-
* Added #to_hash and #from_hash serializer methods to allow larger consumer json constructions more directly.
|
12
|
-
* Opt for string keys during serialization/deserialization.
|
13
|
-
|
14
|
-
#### 0.0.6 - May 11th, 2023
|
15
|
-
|
16
|
-
* Added Season module that can generate full schedules for entire league.
|
17
|
-
* Draft::Event does not have identity anymore (no current tangible benefit).
|
18
|
-
|
19
|
-
#### 0.0.5 - May 5th, 2023
|
20
|
-
|
21
|
-
* Remove the notion of Team in favor of a flat front office.
|
22
|
-
#### 0.0.4 - May 5th, 2023
|
23
|
-
|
24
|
-
* Add ability to skip draft picks using `Basketball::Draft::Room#skip!`
|
25
|
-
* Add ability to output event full draft event log using CLI: `basketball-draft -i tmp/draft-wip.json -l`
|
26
|
-
* Add ability to skip draft picks using CLI: `basketball-draft -i tmp/draft-wip.json -x 1`
|
27
|
-
|
28
|
-
#### 0.0.3 - May 5th, 2023
|
29
|
-
|
30
|
-
* `Draft::Room#sim!` should return events
|
31
|
-
* Added `Draft::Room#undrafted_player_search`
|
32
|
-
|
33
|
-
#### 0.0.2 - May 4th, 2023
|
34
|
-
|
35
|
-
* Remove autoloading in favor of require statements.
|
36
|
-
|
37
|
-
#### 0.0.1 - May 4th, 2023
|
38
|
-
|
39
|
-
* Initial release with Draft module only
|
1
|
+
Will not be kept track of until stable version 1.0.0
|
data/README.md
CHANGED
@@ -1,14 +1,48 @@
|
|
1
|
-
#
|
1
|
+
# Basketball
|
2
2
|
|
3
3
|
[![Gem Version](https://badge.fury.io/rb/basketball.svg)](https://badge.fury.io/rb/basketball) [![CI](https://github.com/mattruggio/basketball/actions/workflows/ci.yaml/badge.svg)](https://github.com/mattruggio/basketball/actions/workflows/ci.yaml) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
|
4
4
|
|
5
|
-
:warning: **Note:** This is currently in the early phases of initial development. Consider all
|
6
|
-
|
7
|
-
#### Basketball League Game Room
|
5
|
+
:warning: **Note:** This is currently in the early phases of initial development. Consider all < 1 releases as having unstable APIs between versions. A formal 1.0.0 major release will be eventually released which will honor [Semver](https://semver.org/).
|
8
6
|
|
9
7
|
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.
|
10
8
|
|
11
|
-
|
9
|
+
![Architecture](/docs/architecture.png)
|
10
|
+
|
11
|
+
Element | Description
|
12
|
+
:------------ | :-----------
|
13
|
+
**Arena** | Determines exhibition and regular season game outcomes.
|
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 illustrating an example of how to subclass and consume the drafting module.
|
16
|
+
**Calendar** | Stores important boundary dates (preseason start, preseason end, season start, and season end).
|
17
|
+
**Coordinator CLI** | Underlying Ruby class that powers the `basketball-coordinator` script. Basically a terminal wrapper over the Coordinator object.
|
18
|
+
**Coordinator Repository** | Understands how to save and load Coordinator objects from JSON files on disk.
|
19
|
+
**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.
|
20
|
+
**Draft** | Bounded context (sub-module) dealing with running a round-robin player draft for teams.
|
21
|
+
**Exhibition** | Pre-season game which has no impact to team record.
|
22
|
+
**External Dependency** | Some outside system which this library or portions of this library are dependent on.
|
23
|
+
**File Store** | Implements a store that can interact with the underlying File System.
|
24
|
+
**File System** | Local operating system that the CLI will use for persistence.
|
25
|
+
**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.
|
26
|
+
**Game** | Matches up a date with two teams (home and away) to represent a coordinatord match-up.
|
27
|
+
**League Repository** | Understands how to save and load League objects from JSON files on disk.
|
28
|
+
**League** | Describes a league in terms of structure composed of teams and players.
|
29
|
+
**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.
|
30
|
+
**Org** | Bounded context (sub-module) dealing with overall organizational structure of a sports assocation.
|
31
|
+
**Pick** | Result event emitted when a player is automatically or manually selected.
|
32
|
+
**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.
|
33
|
+
**Regular** | Game that counts towards regular season record.
|
34
|
+
**Result** | The outcome of a game (typically with a home and away score).
|
35
|
+
**Room CLI** | Underlying Ruby class that powers the `basketball-room` script. Basically a terminal wrapper for the Room object.
|
36
|
+
**Room Repository** | Understands how to save and load Room objects from JSON files on disk.
|
37
|
+
**Room** | Main object responsible for providing an iterable interface capable of executing a draft, pick by pick.
|
38
|
+
**Scout** | Knows how to stack rank lists of players.
|
39
|
+
**Season** | Bounded context (sub-module) dealing with calendar and matchup generation.
|
40
|
+
**Skip** | Result event emitted when a front office decides to skip a round.
|
41
|
+
**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.
|
42
|
+
**Team Group** | Set of rosters that together form a cohesive league.
|
43
|
+
**Team** | Member of a league and signs players. Has games assigned and played.
|
44
|
+
|
45
|
+
### Installation
|
12
46
|
|
13
47
|
To install through Rubygems:
|
14
48
|
|
@@ -22,176 +56,124 @@ You can also add this to your Gemfile using:
|
|
22
56
|
bundle add basketball
|
23
57
|
````
|
24
58
|
|
25
|
-
|
26
|
-
|
27
|
-
````
|
28
|
-
bundle binstubs basketball
|
29
|
-
````
|
30
|
-
|
31
|
-
## Sub-Modules
|
32
|
-
|
33
|
-
This library is broken down into several bounded contexts that can be consumed either via its Ruby API's or CLI through provided executable scripts:
|
34
|
-
|
35
|
-
![Basketball Architecture - Overview](/docs/architecture/Basketball%20Architecture%20-%20Overview.png)
|
36
|
-
|
37
|
-
#### Command Line Interfaces
|
38
|
-
|
39
|
-
Each module is meant to be interfaced with using its Ruby API by consuming applications. Each module also ships with a CLI script (backed by a module service) which a user can interact with to emulate different portions of the league management process. Technically speaking, the CLI provides an example application built on top of the each individual core module. Each module section below should contain with it example CLI calls.
|
59
|
+
### Draft Module
|
40
60
|
|
41
|
-
|
61
|
+
The input for the main object `Basketball::Draft::Room` is an array of teams (`Basketball::Draft::FrontOffice`) and players (`Basketball::Org::Players`). Once instantiated there are four main methods:
|
42
62
|
|
43
|
-
|
63
|
+
* **Basketball::Draft::Room#sim!**: Simulate the next pick.
|
64
|
+
* **Basketball::Draft::Room#skip!**: Skip the next pick.
|
65
|
+
* **Basketball::Draft::Room#pick!(player)**: Pick an exact player for the current front office.
|
66
|
+
* **Basketball::Draft::Room#sim_rest!**: Simulate the rest of the picks.
|
44
67
|
|
45
|
-
|
68
|
+
#### Command Line Interface
|
46
69
|
|
47
|
-
|
48
|
-
:------------ | :-----------
|
49
|
-
**Draft** | Bounded context (sub-module) dealing with executing an asynchronous draft.
|
50
|
-
**Room** | Aggregate root responsible for providing an iterable interface capable of executing a draft, pick by pick.
|
51
|
-
**Event** | Represents one cycle execution result from the Room.
|
52
|
-
**External Ruby App** | An example consumer for the Draft context.
|
53
|
-
**Front Office** | Identifiable as a team, contains configuration for how to auto-pick draft selections.
|
54
|
-
**League** | Set of rosters that together form a cohesive league.
|
55
|
-
**Pick Event** | Result event emitted when a player is manually selected.
|
56
|
-
**Player** | Identitiable as a person able to be drafted.
|
57
|
-
**Position** | Value object based on position code: PG, SG, SF, PF, and C.
|
58
|
-
**Roster** | Identifiable as a team, set of players that make up a single team.
|
59
|
-
**Sim Event** | Result event emitted when a player is automatically selected by a front office.
|
60
|
-
**Skip Event** | Result event emitted when a front office decides to skip a round.
|
61
|
-
|
62
|
-
#### The Draft CLI
|
63
|
-
|
64
|
-
The draft module's main object: `Basketball::Draft::Room` is a stateful iterator. Each time a CLI command is executed, it's results will be re-saved to disk so the output file can then be used as the next command's input file to string together commands.
|
70
|
+
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.
|
65
71
|
|
66
72
|
###### Generate a Fresh Draft
|
67
73
|
|
68
74
|
```zsh
|
69
|
-
basketball-
|
75
|
+
basketball-room -o tmp/draft.json
|
70
76
|
```
|
71
77
|
|
72
78
|
###### N Top Available Players
|
73
79
|
|
74
80
|
```zsh
|
75
|
-
basketball-
|
81
|
+
basketball-room -i tmp/draft.json -t 10
|
76
82
|
```
|
77
83
|
|
78
84
|
###### N Top Available Players for a Position
|
79
85
|
|
80
86
|
```zsh
|
81
|
-
basketball-
|
87
|
+
basketball-room -i tmp/draft.json -t 10 -q PG
|
82
88
|
```
|
83
89
|
|
84
|
-
###### Output
|
90
|
+
###### Output League
|
85
91
|
|
86
92
|
```zsh
|
87
|
-
basketball-
|
93
|
+
basketball-room -i tmp/draft.json -l
|
88
94
|
```
|
89
95
|
|
90
96
|
###### Output Event Log
|
91
97
|
|
92
98
|
```zsh
|
93
|
-
basketball-
|
99
|
+
basketball-room -i tmp/draft.json -e
|
94
100
|
```
|
95
101
|
|
96
102
|
###### Simulate N Picks
|
97
103
|
|
98
104
|
```zsh
|
99
|
-
basketball-
|
105
|
+
basketball-room -i tmp/draft.json -s 10
|
100
106
|
```
|
101
107
|
|
102
108
|
###### Skip N Picks
|
103
109
|
|
104
110
|
```zsh
|
105
|
-
basketball-
|
111
|
+
basketball-room -i tmp/draft.json -x 10
|
106
112
|
```
|
107
113
|
|
108
114
|
###### Pick Players
|
109
115
|
|
110
116
|
```zsh
|
111
|
-
basketball-
|
117
|
+
basketball-room -i tmp/draft.json -p P-100,P-200,P-300
|
112
118
|
```
|
113
119
|
|
114
120
|
###### Simulate the Rest of the Draft
|
115
121
|
|
116
122
|
```zsh
|
117
|
-
basketball-
|
123
|
+
basketball-room -i tmp/draft.json -a
|
118
124
|
```
|
119
125
|
|
120
126
|
###### Help Menu
|
121
127
|
|
122
128
|
```zsh
|
123
|
-
basketball-
|
129
|
+
basketball-room -h
|
124
130
|
```
|
125
131
|
|
126
|
-
|
127
|
-
|
128
|
-
The Season module is meant to take a League (conferences/divisions/teams) and turn it into a Calendar. This Calendar creation is atomic - the full calendar will be generated completely all in one call. Here is a cartoon showing the major components:
|
129
|
-
|
130
|
-
![Basketball Architecture - Season](/docs/architecture/Basketball%20Architecture%20-%20Season.png)
|
131
|
-
|
132
|
-
Element | Description
|
133
|
-
:------------ | :-----------
|
134
|
-
**Away Team** | Team object designated as the away team for a Game.
|
135
|
-
**Calendar Serializer** | Understands how to serialize and deserialize a Calendar object.
|
136
|
-
**Calendar** | Hold a calendar for a year season. Pass in a year and the Calendar will know how to mark important boundary dates (preseason start, preseason end, season start, and season end) and it knows how to ensure Calendar correctness regarding dates.
|
137
|
-
**Conference** | Describes a conference in terms of structure; composed of an array of divisions (there can only 3).
|
138
|
-
**Coordinator** | Service which can generate a Calendar from a League.
|
139
|
-
**Division** | Describes a division in terms of structure; composed of an array of teams (there can only 5).
|
140
|
-
**Game** | Matches up a date with two teams (home and away) to represent a scheduled matchup.
|
141
|
-
**Home Team** | Team object designated as the home team for a Game.
|
142
|
-
**League Serializer** | Understands how to serialize and deserialize a League object.
|
143
|
-
**League** | Describes a league in terms of structure; composed of an array conferences (there can only be 2).
|
144
|
-
**Season** | Bounded context (sub-module) dealing with calendar and matchup generation.
|
145
|
-
**Team** | Identified by an ID and described by a name: represents a basketball team that can be scheduled.
|
146
|
-
|
147
|
-
#### The Season CLI
|
132
|
+
### Season Module
|
148
133
|
|
149
|
-
|
134
|
+
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:
|
150
135
|
|
151
|
-
|
152
|
-
|
153
|
-
```
|
136
|
+
* **Basketball::Season::Coordinator#sim!**: Simulate the next day of games.
|
137
|
+
* **Basketball::Season::Coordinator#sim_rest!**: Simulate the rest of the games.
|
154
138
|
|
155
|
-
|
139
|
+
#### Command Line Interface
|
156
140
|
|
157
|
-
|
158
|
-
basketball-season-scheduling -i tmp/league.json -o tmp/calendar.json
|
159
|
-
```
|
141
|
+
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.
|
160
142
|
|
161
|
-
###### Generate
|
143
|
+
###### Generate Random coordinator
|
162
144
|
|
163
145
|
```zsh
|
164
|
-
basketball-
|
146
|
+
exe/basketball-coordinator -o tmp/coordinator.json
|
165
147
|
```
|
166
148
|
|
167
|
-
######
|
149
|
+
###### Sim One Day and Save to New File
|
168
150
|
|
169
151
|
```zsh
|
170
|
-
basketball-
|
152
|
+
exe/basketball-coordinator -i tmp/coordinator.json -o tmp/coordinator2.json -d 1
|
171
153
|
```
|
172
154
|
|
173
|
-
###### Output
|
155
|
+
###### Output Event Log
|
174
156
|
|
175
157
|
```zsh
|
176
|
-
basketball-
|
158
|
+
exe/basketball-coordinator -i tmp/coordinator.json -e
|
177
159
|
```
|
178
160
|
|
179
|
-
######
|
161
|
+
###### Sim Two Days and Save To Input File
|
180
162
|
|
181
163
|
```zsh
|
182
|
-
basketball-
|
164
|
+
exe/basketball-coordinator -i tmp/coordinator.json -d 2
|
183
165
|
```
|
184
166
|
|
185
|
-
######
|
167
|
+
###### Sim Rest of Calendar
|
186
168
|
|
187
169
|
```zsh
|
188
|
-
basketball-
|
170
|
+
exe/basketball-coordinator -i tmp/coordinator.json -a
|
189
171
|
```
|
190
172
|
|
191
173
|
###### Help Menu
|
192
174
|
|
193
175
|
```zsh
|
194
|
-
basketball-
|
176
|
+
basketball-coordinator -h
|
195
177
|
```
|
196
178
|
|
197
179
|
## Contributing
|
data/basketball.gemspec
CHANGED
@@ -5,19 +5,17 @@ require './lib/basketball/version'
|
|
5
5
|
Gem::Specification.new do |s|
|
6
6
|
s.name = 'basketball'
|
7
7
|
s.version = Basketball::VERSION
|
8
|
-
s.summary = 'Basketball
|
8
|
+
s.summary = 'Basketball Simulation Domain Model'
|
9
9
|
|
10
10
|
s.description = <<-DESC
|
11
|
-
This library is meant to serve as the domain for a basketball league/season simulator/turn-based game.
|
12
|
-
It models core ideas such as: players, general managers, draft strategy, drafting, season generation, season simultation,
|
13
|
-
playoff generation, playoff simulation, and more.
|
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.
|
14
12
|
DESC
|
15
13
|
|
16
14
|
s.authors = ['Matthew Ruggio']
|
17
15
|
s.email = ['mattruggio@icloud.com']
|
18
16
|
s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(.github|bin|docs|spec)/}) }
|
19
17
|
s.bindir = 'exe'
|
20
|
-
s.executables = %w[basketball-
|
18
|
+
s.executables = %w[basketball-coordinator basketball-room]
|
21
19
|
s.homepage = 'https://github.com/mattruggio/basketball'
|
22
20
|
s.license = 'MIT'
|
23
21
|
s.metadata = {
|
@@ -31,6 +29,5 @@ Gem::Specification.new do |s|
|
|
31
29
|
|
32
30
|
s.required_ruby_version = '>= 3.2.1'
|
33
31
|
|
34
|
-
s.add_dependency('faker', '~>3.2')
|
35
32
|
s.add_dependency('slop', '~>4.10')
|
36
33
|
end
|
@@ -0,0 +1,250 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Basketball
|
4
|
+
module App
|
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
|
11
|
+
#
|
12
|
+
# exe/basketball-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 preseason (#{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(team_count: 2, players_per_team_count: 4)
|
108
|
+
Org::League.new.tap do |league|
|
109
|
+
team_count.times do |i|
|
110
|
+
team = Org::Team.new(id: "T-#{i}")
|
111
|
+
|
112
|
+
players_per_team_count.times do |j|
|
113
|
+
player = Org::Player.new(
|
114
|
+
id: "T-#{i}-P-#{j}",
|
115
|
+
overall: rand(20..100),
|
116
|
+
position: Org::Position.random
|
117
|
+
)
|
118
|
+
|
119
|
+
team.sign!(player)
|
120
|
+
end
|
121
|
+
|
122
|
+
league.register!(team)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def make_calendar(league:)
|
128
|
+
preseason_start_date = Date.new(2000, 1, 1)
|
129
|
+
season_start_date = Date.new(2000, 1, 11)
|
130
|
+
|
131
|
+
exhibitions = make_games(
|
132
|
+
start_date: preseason_start_date,
|
133
|
+
count: 10,
|
134
|
+
league:,
|
135
|
+
game_class: Season::Exhibition
|
136
|
+
)
|
137
|
+
|
138
|
+
regulars = make_games(
|
139
|
+
start_date: season_start_date,
|
140
|
+
count: 10,
|
141
|
+
league:,
|
142
|
+
game_class: Season::Regular
|
143
|
+
)
|
144
|
+
|
145
|
+
Season::Calendar.new(
|
146
|
+
preseason_start_date:,
|
147
|
+
preseason_end_date: Date.new(2000, 1, 10),
|
148
|
+
season_start_date:,
|
149
|
+
season_end_date: Date.new(2000, 1, 20),
|
150
|
+
games: exhibitions + regulars
|
151
|
+
)
|
152
|
+
end
|
153
|
+
|
154
|
+
def make_games(start_date:, count:, league:, game_class:)
|
155
|
+
count.times.map do |i|
|
156
|
+
home_team, away_team = league.teams.sample(2)
|
157
|
+
|
158
|
+
game_class.new(
|
159
|
+
date: start_date + i,
|
160
|
+
home_opponent: Season::Opponent.new(id: home_team.id),
|
161
|
+
away_opponent: Season::Opponent.new(id: away_team.id)
|
162
|
+
)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def make_coordinator
|
167
|
+
league = make_league
|
168
|
+
current_date = Date.new(2000, 1, 1)
|
169
|
+
calendar = make_calendar(league:)
|
170
|
+
|
171
|
+
Season::Coordinator.new(
|
172
|
+
calendar:,
|
173
|
+
current_date:,
|
174
|
+
league:
|
175
|
+
)
|
176
|
+
end
|
177
|
+
|
178
|
+
def read
|
179
|
+
coordinator =
|
180
|
+
if input?
|
181
|
+
io.puts("Coordinator loaded from: #{input}")
|
182
|
+
|
183
|
+
coordinator_repository.load(input)
|
184
|
+
else
|
185
|
+
io.puts('Input path was not provided, generating fresh coordinator')
|
186
|
+
|
187
|
+
make_coordinator
|
188
|
+
end
|
189
|
+
|
190
|
+
io.puts("Current Date: #{coordinator.current_date}")
|
191
|
+
|
192
|
+
coordinator
|
193
|
+
end
|
194
|
+
|
195
|
+
def input
|
196
|
+
opts[:input]
|
197
|
+
end
|
198
|
+
|
199
|
+
def input?
|
200
|
+
!no_input?
|
201
|
+
end
|
202
|
+
|
203
|
+
def no_input?
|
204
|
+
input.to_s.empty?
|
205
|
+
end
|
206
|
+
|
207
|
+
def output
|
208
|
+
opts[:output]
|
209
|
+
end
|
210
|
+
|
211
|
+
def no_output?
|
212
|
+
output.to_s.empty?
|
213
|
+
end
|
214
|
+
|
215
|
+
def output?
|
216
|
+
!no_output?
|
217
|
+
end
|
218
|
+
|
219
|
+
def events(coordinator)
|
220
|
+
return unless opts[:events]
|
221
|
+
|
222
|
+
io.puts
|
223
|
+
io.puts('Event Log')
|
224
|
+
|
225
|
+
puts coordinator.results
|
226
|
+
end
|
227
|
+
|
228
|
+
def slop_parse(args)
|
229
|
+
Slop.parse(args) do |o|
|
230
|
+
o.banner = 'Usage: basketball-coordinator [options] ...'
|
231
|
+
|
232
|
+
input_description = <<~DESC
|
233
|
+
Path to load the Coordinator from. If omitted then a new coordinator will be created.
|
234
|
+
DESC
|
235
|
+
|
236
|
+
o.string '-i', '--input', input_description.chomp
|
237
|
+
o.string '-o', '--output', 'Path to save updated coordinator. If omitted then the input path will be used.'
|
238
|
+
o.integer '-d', '--days', 'Number of days to simulate'
|
239
|
+
o.bool '-a', '--sim-all', 'Simulate the rest of the coordinator', default: false
|
240
|
+
o.bool '-e', '--events', 'Output event log.', default: false
|
241
|
+
|
242
|
+
o.on '-h', '--help', 'Print out help, like this is doing right now.' do
|
243
|
+
io.puts(o)
|
244
|
+
exit
|
245
|
+
end
|
246
|
+
end.to_h
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|