# Sports Manager Gem The `sports-manager` gem is a powerful tool designed to generate and manage tournament schedules. It handles complex scheduling tasks, considering various constraints such as court availability, game length, rest breaks, and participant availability. Under the hood, it leverages the [`csp-resolver`](https://github.com/Rebase-BR/csp-resolver) gem to solve these complex Constraint Satisfaction Problems (CSPs). ## Getting Started ### Requirements - Ruby >= 2.5.8 ### Installing You can install using the following command: ```bash gem install "sports-manager" ``` Then install it: ```bash $ bundle install ``` ## Usage ### Setting Up a Tournament To set up a tournament, you need to provide the following information: ```ruby require 'sports-manager' days = { '2023-09-09': { start: 9, end: 20 }, '2023-09-10': { start: 9, end: 13 } } courts = 2 game_length = 60 rest_break = 30 single_day_matches = false subscriptions = { mens_single: [ { id: 1, name: 'João' }, { id: 2, name: 'Marcelo' }, { id: 3, name: 'José' }, { id: 4, name: 'Pedro' }, { id: 5, name: 'Carlos' }, { id: 6, name: 'Leandro' }, { id: 7, name: 'Leonardo' }, { id: 8, name: 'Cláudio' }, { id: 9, name: 'Alexandre' }, { id: 10, name: 'Daniel' }, { id: 11, name: 'Marcos' }, { id: 12, name: 'Henrique' }, { id: 13, name: 'Joaquim' }, { id: 14, name: 'Alex' }, { id: 15, name: 'Bruno' }, { id: 16, name: 'Fábio' } ] } matches = { mens_single: [ [1, 16], [2, 15], [3, 14], [4, 13], [5, 12], [6, 11], [7, 10], [8, 9] ] } solution = SportsManager::TournamentGenerator.new(format: :cli) .add_days(days) .add_courts(courts) .add_game_length(game_length) .add_rest_break(rest_break) .enable_single_day_matches(single_day_matches) .add_subscriptions(subscriptions) .single_elimination_algorithm .add_matches(matches) ``` You can also pass already generated matches to the generator, it's useful when your already have the matches generated by another system, but you still want to generate the schedule. ```ruby params = { when: { '2023-09-09': { start: 9, end: 20 }, '2023-09-10': { start: 9, end: 13 } }, courts: 2, game_length: 60, rest_brake: 30, single_day_matches: false, subscriptions: { mens_single: [ { id: 1, name: 'João' }, { id: 2, name: 'Marcelo' }, { id: 3, name: 'José' }, { id: 4, name: 'Pedro' }, { id: 5, name: 'Carlos' }, { id: 6, name: 'Leandro' }, { id: 7, name: 'Leonardo' }, { id: 8, name: 'Cláudio' }, { id: 9, name: 'Alexandre' }, { id: 10, name: 'Daniel' }, { id: 11, name: 'Marcos' }, { id: 12, name: 'Henrique' }, { id: 13, name: 'Joaquim' }, { id: 14, name: 'Alex' }, { id: 15, name: 'Bruno' }, { id: 16, name: 'Fábio' } ] }, matches: { mens_single: [ { id: 1, participants: [1, 16], }, { id: 2, participants: [2, 15], }, { id: 3, participants: [3, 14], }, { id: 4, participants: [4, 13], }, { id: 5, participants: [5, 12], }, { id: 6, participants: [6, 11], }, { id: 7, participants: [7, 10], }, { id: 8, participants: [8, 9], }, { id: 9, depends_on: [1, 2], round: 1 }, { id: 10, depends_on: [3, 4], round: 1 }, { id: 11, depends_on: [5, 6], round: 1 }, { id: 12, depends_on: [7, 8], round: 1 }, { id: 13, depends_on: [9, 10], round: 2 }, { id: 14, depends_on: [11, 12], round: 2}, { id: 15, depends_on: [13, 14], round: 2}, ] } } ``` #### Configuration methods - `add_days(days)`: Adds the tournament days. - `add_day(day, start, end)`: Adds a single tournament day. - `add_courts(courts)`: Adds the number of available courts. - `add_game_length(game_length)`: Adds the duration of each game in minutes. - `add_rest_break(rest_break)`: Adds the rest time between player matches in minutes. - `enable_single_day_matches(single_day_matches)`: Sets if all matches should be on the same day. - `add_subscriptions(subscriptions)`: Adds the players or teams participating in each category. - `add_subscription(category, subscription)`: Adds a single player or team to a category. - `add_subscriptions_per_category(subscriptions_per_category)`: Adds the players or teams participating per category. - `add_matches(matches)`: Adds the first matchups for each category. - `add_match(category, match)`: Adds a single match to a category. - `add_matches_per_category(category, matches_per_category)`: Adds the first matchups per category. - `single_elimination_algorithm`: Sets the single elimination algorithm(this option is already default). ### Running Example Tournaments The gem comes with predefined example tournaments: ```ruby solution = SportsManager::TournamentGenerator.example(:simple) solution = SportsManager::TournamentGenerator.example(:complex) #complete, minimal, ... ``` ### Output Formats You can choose different output formats: ```ruby # CLI format (default) SportsManager::TournamentGenerator.new(format: :cli) # Mermaid format (for visual diagrams) SportsManager::TournamentGenerator.new(format: :mermaid) ``` #### Output examples ```ruby require 'sports-manager' days = { '2023-09-09': { start: 9, end: 20 }, '2023-09-10': { start: 9, end: 13 } } courts = 2 game_length = 60 rest_break = 30 single_day_matches = false subscriptions = { mens_single: [ { id: 1, name: 'João' }, { id: 2, name: 'Marcelo' }, { id: 3, name: 'José' }, { id: 4, name: 'Pedro' }, { id: 5, name: 'Carlos' }, { id: 6, name: 'Leandro' }, { id: 7, name: 'Leonardo' }, { id: 8, name: 'Cláudio' }, { id: 9, name: 'Alexandre' }, { id: 10, name: 'Daniel' }, { id: 11, name: 'Marcos' }, { id: 12, name: 'Henrique' }, { id: 13, name: 'Joaquim' }, { id: 14, name: 'Alex' }, { id: 15, name: 'Bruno' }, { id: 16, name: 'Fábio' } ] } matches = { mens_single: [ [1, 16], [2, 15], [3, 14], [4, 13], [5, 12], [6, 11], [7, 10], [8, 9] ] } solution = SportsManager::TournamentGenerator.new(format: :cli) .add_days(days) .add_courts(courts) .add_game_length(game_length) .add_rest_break(rest_break) .enable_single_day_matches(single_day_matches) .add_subscriptions(subscriptions) .single_elimination_algorithm .add_matches(matches) .call ``` ```bash Tournament Timetable: Solution 1 category | id | round | participants | court | time ------------|----|-------|-----------------------|-------|--------------- mens_single | 1 | 0 | João vs. Fábio | 0 | 09/09 at 09:00 mens_single | 2 | 0 | Marcelo vs. Bruno | 1 | 09/09 at 09:00 mens_single | 3 | 0 | José vs. Alex | 0 | 09/09 at 10:00 mens_single | 4 | 0 | Pedro vs. Joaquim | 1 | 09/09 at 10:00 mens_single | 5 | 0 | Carlos vs. Henrique | 0 | 09/09 at 11:00 mens_single | 6 | 0 | Leandro vs. Marcos | 1 | 09/09 at 11:00 mens_single | 7 | 0 | Leonardo vs. Daniel | 0 | 09/09 at 12:00 mens_single | 8 | 0 | Cláudio vs. Alexandre | 1 | 09/09 at 12:00 mens_single | 9 | 1 | M1 vs. M2 | 0 | 09/09 at 13:00 mens_single | 10 | 1 | M3 vs. M4 | 1 | 09/09 at 13:00 mens_single | 11 | 1 | M5 vs. M6 | 0 | 09/09 at 14:00 mens_single | 12 | 1 | M7 vs. M8 | 1 | 09/09 at 14:00 mens_single | 13 | 2 | M9 vs. M10 | 0 | 09/09 at 15:00 mens_single | 14 | 2 | M11 vs. M12 | 1 | 09/09 at 15:30 mens_single | 15 | 2 | M13 vs. M14 | 0 | 09/09 at 17:00 Total solutions: 1 ``` ```ruby require 'sports-manager' days = { '2023-09-09': { start: 9, end: 20 }, '2023-09-10': { start: 9, end: 13 } } courts = 2 game_length = 60 rest_break = 30 single_day_matches = false subscriptions = { mens_single: [ { id: 1, name: 'João' }, { id: 2, name: 'Marcelo' }, { id: 3, name: 'José' }, { id: 4, name: 'Pedro' }, { id: 5, name: 'Carlos' }, { id: 6, name: 'Leandro' }, { id: 7, name: 'Leonardo' }, { id: 8, name: 'Cláudio' }, { id: 9, name: 'Alexandre' }, { id: 10, name: 'Daniel' }, { id: 11, name: 'Marcos' }, { id: 12, name: 'Henrique' }, { id: 13, name: 'Joaquim' }, { id: 14, name: 'Alex' }, { id: 15, name: 'Bruno' }, { id: 16, name: 'Fábio' } ] } matches = { mens_single: [ [1, 16], [2, 15], [3, 14], [4, 13], [5, 12], [6, 11], [7, 10], [8, 9] ] } solution = SportsManager::TournamentGenerator.new(format: :mermaid) .add_days(days) .add_courts(courts) .add_game_length(game_length) .add_rest_break(rest_break) .enable_single_day_matches(single_day_matches) .add_subscriptions(subscriptions) .single_elimination_algorithm .add_matches(matches) .call ``` ```bash Solutions: -------------------------------------------------------------------------------- Solutions 1 Gantt: --- displayMode: compact --- gantt title Tournament Schedule dateFormat DD/MM HH:mm axisFormat %H:%M tickInterval 1hour section 0 MS M1: 09/09 09:00, 1h MS M3: 09/09 10:00, 1h MS M5: 09/09 11:00, 1h MS M7: 09/09 12:00, 1h MS M9: 09/09 13:00, 1h MS M11: 09/09 14:00, 1h MS M13: 09/09 15:00, 1h MS M15: 09/09 17:00, 1h section 1 MS M2: 09/09 09:00, 1h MS M4: 09/09 10:00, 1h MS M6: 09/09 11:00, 1h MS M8: 09/09 12:00, 1h MS M10: 09/09 13:00, 1h MS M12: 09/09 14:00, 1h MS M14: 09/09 15:30, 1h Graph: graph LR classDef court0 fill:#A9F9A9, color:#000000 classDef court1 fill:#4FF7DE, color:#000000 subgraph colorscheme direction LR COURT0:::court0 COURT1:::court1 end subgraph mens_single direction LR mens_single_1[1\nJoão vs. Fábio\n09/09 09:00]:::court0 mens_single_2[2\nMarcelo vs. Bruno\n09/09 09:00]:::court1 mens_single_3[3\nJosé vs. Alex\n09/09 10:00]:::court0 mens_single_4[4\nPedro vs. Joaquim\n09/09 10:00]:::court1 mens_single_5[5\nCarlos vs. Henrique\n09/09 11:00]:::court0 mens_single_6[6\nLeandro vs. Marcos\n09/09 11:00]:::court1 mens_single_7[7\nLeonardo vs. Daniel\n09/09 12:00]:::court0 mens_single_8[8\nCláudio vs. Alexandre\n09/09 12:00]:::court1 mens_single_9[9\nM1 vs. M2\n09/09 13:00]:::court0 mens_single_10[10\nM3 vs. M4\n09/09 13:00]:::court1 mens_single_11[11\nM5 vs. M6\n09/09 14:00]:::court0 mens_single_12[12\nM7 vs. M8\n09/09 14:00]:::court1 mens_single_13[13\nM9 vs. M10\n09/09 15:00]:::court0 mens_single_14[14\nM11 vs. M12\n09/09 15:30]:::court1 mens_single_15[15\nM13 vs. M14\n09/09 17:00]:::court0 mens_single_1 --> mens_single_9 mens_single_2 --> mens_single_9 mens_single_3 --> mens_single_10 mens_single_4 --> mens_single_10 mens_single_5 --> mens_single_11 mens_single_6 --> mens_single_11 mens_single_7 --> mens_single_12 mens_single_8 --> mens_single_12 mens_single_9 --> mens_single_13 mens_single_10 --> mens_single_13 mens_single_11 --> mens_single_14 mens_single_12 --> mens_single_14 mens_single_13 --> mens_single_15 mens_single_14 --> mens_single_15 end -------------------------------------------------------------------------------- Total solutions: 1 ``` ```mermaid graph LR classDef court0 fill:#A9F9A9, color:#000000 classDef court1 fill:#4FF7DE, color:#000000 subgraph colorscheme direction LR COURT0:::court0 COURT1:::court1 end subgraph mens_single direction LR mens_single_1[1\nJoão vs. Fábio\n09/09 09:00]:::court0 mens_single_2[2\nMarcelo vs. Bruno\n09/09 09:00]:::court1 mens_single_3[3\nJosé vs. Alex\n09/09 10:00]:::court0 mens_single_4[4\nPedro vs. Joaquim\n09/09 10:00]:::court1 mens_single_5[5\nCarlos vs. Henrique\n09/09 11:00]:::court0 mens_single_6[6\nLeandro vs. Marcos\n09/09 11:00]:::court1 mens_single_7[7\nLeonardo vs. Daniel\n09/09 12:00]:::court0 mens_single_8[8\nCláudio vs. Alexandre\n09/09 12:00]:::court1 mens_single_9[9\nM1 vs. M2\n09/09 13:00]:::court0 mens_single_10[10\nM3 vs. M4\n09/09 13:00]:::court1 mens_single_11[11\nM5 vs. M6\n09/09 14:00]:::court0 mens_single_12[12\nM7 vs. M8\n09/09 14:00]:::court1 mens_single_13[13\nM9 vs. M10\n09/09 15:00]:::court0 mens_single_14[14\nM11 vs. M12\n09/09 15:30]:::court1 mens_single_15[15\nM13 vs. M14\n09/09 17:00]:::court0 mens_single_1 --> mens_single_9 mens_single_2 --> mens_single_9 mens_single_3 --> mens_single_10 mens_single_4 --> mens_single_10 mens_single_5 --> mens_single_11 mens_single_6 --> mens_single_11 mens_single_7 --> mens_single_12 mens_single_8 --> mens_single_12 mens_single_9 --> mens_single_13 mens_single_10 --> mens_single_13 mens_single_11 --> mens_single_14 mens_single_12 --> mens_single_14 mens_single_13 --> mens_single_15 mens_single_14 --> mens_single_15 end ``` ## Contributing See our [CONTRIBUTING](./CONTRIBUTING.md) guidelines. ## Code of Conduct We expect that everyone participating in any way with this project follows our [Code of Conduct](./CODE_OF_CONDUCT.md). ## License This project is licensed under the [MIT License](MIT-LICENSE).