social_tournament/lib.rs
1//!
2//! This is a library for creating tournament schedules for the sport I love.
3//!
4//! 🏓 table tennis 🏓
5//!
6//! The focus is on meeting as many opponents and teammates as possible during the tournament. One can draw a single or double tournament.
7//! After the games are drawn, you can decide whether you also want to use the automatic table distribution algorithm.
8use crate::double::{DrawOption, draw_doubles};
9use crate::table::{DistributionOption, Table, distribute_tables};
10use crate::single::draw_singles;
11use crate::Error::DefaultError;
12use thiserror::Error;
13use crate::pdf::language::Language;
14use crate::pdf::route_card::create_route_cards_for_double;
15
16#[macro_use]
17extern crate lazy_static;
18
19pub mod single;
20pub mod double;
21pub mod table;
22pub mod pdf;
23
24type Result<T> = std::result::Result<T, Error>;
25
26/// Default [enum@Error] for this crate
27#[derive(Error, Debug)]
28pub enum Error {
29 #[error("default social tournament error")]
30 DefaultError(),
31}
32
33/// This enum describes all relevant parameters for the tournament. Choose between a single
34/// and a double tournament. How many players and rounds do you have? If you are playing doubles,
35/// what is your preferred [DrawOption] if there are not enough players for "full" double pairs.
36/// How many tables do you have? Specify a [TableConfig] to determine how many tables you can set up
37/// in your room or gym and choose a [DistributionOption] if you have more matches in a round than
38/// tables available.
39#[derive(PartialEq, Clone, Debug)]
40pub enum TournamentConfig {
41 Single {
42 number_of_players: usize,
43 number_of_rounds: usize,
44 table_config: TableConfig,
45 },
46 Double {
47 number_of_players: usize,
48 number_of_rounds: usize,
49 draw_option: Option<DrawOption>,
50 table_config: TableConfig,
51 },
52}
53
54/// Specify a [TableConfig] to determine how many tables you can set up
55/// in your room or gym and choose a [DistributionOption] if you have more matches in a round than
56/// tables available.
57#[derive(PartialEq, Clone, Debug)]
58pub struct TableConfig {
59 pub available_tables: usize,
60 pub distribution_option: Option<DistributionOption>,
61}
62
63/// Enum that represent one match.
64#[derive(PartialEq, Clone, Debug)]
65pub enum Match {
66 /// Struct that represent a single match. It holds the opponents `a` and `b`.
67 /// The unique numbers is the id of the corresponding player.
68 SingleMatch { a: usize, b: usize },
69 /// Struct that represent a double match. It holds the pairs `double_a` and `double_b`.
70 /// The tuple contains two numbers, the unique player ids.
71 DoubleMatch { double_a: (usize, usize), double_b: (usize, usize) },
72}
73
74/// Struct for a tournament that represent one round. It holds the `round_number`
75/// and the `matches` that take place in this round. Matches are a list of [Match].
76#[derive(Debug, Clone)]
77pub struct Round {
78 pub round_number: usize,
79 pub matches: Vec<Match>,
80}
81
82/// This struct provides the interface to `draw` the tournament, `distribute` the tables and
83/// `create_route_cards_pdf`. All results are stored in this instance and can be read out
84/// by accessing the corresponding field.
85/// `rounds` contains the drawn rounds, `tables` contains the distributed tables and `pdf` contains
86/// a pdf as a byte array.
87#[derive(Debug, Clone)]
88pub struct SocialTournament {
89 config: TournamentConfig,
90 pub rounds: Option<Vec<Round>>,
91 pub tables: Option<Vec<Vec<Table>>>,
92 pub pdf: Option<Vec<u8>>,
93}
94
95impl SocialTournament {
96
97 /// This method is used to get a new instance of [SocialTournament]. You have to provide a
98 /// [TournamentConfig].
99 pub fn new(config: TournamentConfig) -> SocialTournament {
100 SocialTournament { config, rounds: None, tables: None, pdf: None }
101 }
102
103 /// Interface to "create" a tournament.
104 ///
105 /// For a given `number_of_players` and `number_of_rounds` in [TournamentConfig] it returns a
106 /// schedule of Rounds with the corresponding matches.
107 ///
108 /// # Example Single Tournament
109 ///
110 /// For `number_of_rounds` < `number_of_players` the round
111 /// robin algorithm ensures, that one does not face an opponents twice. For
112 /// `number_of_rounds` >= `number_of_players` the round robin is calculated one more round.
113 /// For an odd number of players, the algorithm calculates with `number_of_players` + 1.
114 /// So you have to make sure that the player who plays against the highest number has a bye.
115 /// ```
116 /// use social_tournament::{Round, SocialTournament, TournamentConfig, TableConfig};
117 ///
118 /// let mut tournament = SocialTournament::new(TournamentConfig::Single {
119 /// number_of_players: 12,
120 /// number_of_rounds: 2,
121 /// table_config: TableConfig { available_tables: 10, distribution_option: None }
122 /// });
123 ///
124 /// tournament.draw().unwrap();
125 /// let rounds: Vec<Round> = tournament.rounds.unwrap();
126 /// /*
127 /// Creates:
128 /// Round number: 0
129 /// SingleMatch { a: 0, b: 9 }
130 /// SingleMatch { a: 1, b: 8 }
131 /// SingleMatch { a: 2, b: 7 }
132 /// SingleMatch { a: 3, b: 6 }
133 /// SingleMatch { a: 4, b: 5 }
134 /// --------------
135 /// Round number: 1
136 /// SingleMatch { a: 1, b: 9 }
137 /// SingleMatch { a: 2, b: 0 }
138 /// SingleMatch { a: 3, b: 8 }
139 /// SingleMatch { a: 4, b: 7 }
140 /// SingleMatch { a: 5, b: 6 }
141 /// --------------
142 /// ...
143 /// */
144 /// ```
145 /// # Example Double Tournament
146 ///
147 /// For number of players that are not completely divisible by 4 you can choose between three
148 /// [DrawOption].
149 /// Depending on the selected option you can have doubles with only 3 players, single matches or
150 /// player with byes. You have to make sure that the player ids >= `number_of_players` in the
151 /// schedule post processed correctly. So that you can mark them as byes for example.
152 ///
153 /// ```
154 /// use social_tournament::{Round, SocialTournament, TournamentConfig, TableConfig};
155 ///
156 /// let mut tournament = SocialTournament::new(TournamentConfig::Double {
157 /// number_of_players: 24,
158 /// number_of_rounds: 2,
159 /// draw_option: None,
160 /// table_config: TableConfig { available_tables: 10, distribution_option: None }
161 /// });
162 ///
163 /// tournament.draw().unwrap();
164 /// let rounds: Vec<Round> = tournament.rounds.unwrap();
165 /// /*
166 /// Creates:
167 /// Round number: 0
168 /// DoubleMatch { double_a: (2, 37), double_b: (1, 38) }
169 /// DoubleMatch { double_a: (3, 36), double_b: (4, 35) }
170 /// DoubleMatch { double_a: (5, 34), double_b: (6, 33) }
171 /// DoubleMatch { double_a: (7, 32), double_b: (8, 31) }
172 /// DoubleMatch { double_a: (9, 30), double_b: (10, 29) }
173 /// DoubleMatch { double_a: (11, 28), double_b: (12, 27) }
174 /// DoubleMatch { double_a: (13, 26), double_b: (14, 25) }
175 /// DoubleMatch { double_a: (15, 24), double_b: (16, 23) }
176 /// DoubleMatch { double_a: (17, 22), double_b: (18, 21) }
177 /// --------------
178 /// Round number: 1
179 /// DoubleMatch { double_a: (20, 21), double_b: (2, 0) }
180 /// DoubleMatch { double_a: (3, 38), double_b: (7, 34) }
181 /// DoubleMatch { double_a: (4, 37), double_b: (6, 35) }
182 /// DoubleMatch { double_a: (5, 36), double_b: (9, 32) }
183 /// DoubleMatch { double_a: (8, 33), double_b: (10, 31) }
184 /// DoubleMatch { double_a: (11, 30), double_b: (15, 26) }
185 /// DoubleMatch { double_a: (12, 29), double_b: (14, 27) }
186 /// DoubleMatch { double_a: (13, 28), double_b: (17, 24) }
187 /// DoubleMatch { double_a: (16, 25), double_b: (18, 23) }
188 /// --------------
189 /// ...
190 /// */
191 /// ```
192 pub fn draw(&mut self) -> Result<()> {
193 match self.config.clone() {
194 TournamentConfig::Single { number_of_players, number_of_rounds, .. } => {
195 self.rounds = Some(draw_singles(number_of_players, number_of_rounds))
196 }
197 TournamentConfig::Double { number_of_players, number_of_rounds, draw_option, .. } => {
198 self.rounds = Some(draw_doubles(number_of_players, number_of_rounds, draw_option))
199 }
200 }
201 Ok(())
202 }
203
204
205 /// Public interface to distribute the tables in a room or gym for the drawn tournament.
206 ///
207 /// Provides an interface to pass the drawn tournament [Round] and the number of your
208 /// `available_tables`.
209 /// Sometimes you may have more tables, but not enough space. Specify how
210 /// many [Table] you can provide for the tournament in your room or gym. The algorithm ensures
211 /// that enough sub-rounds are formed. You can specify the forming method by providing the
212 /// [DistributionOption]. Depending on the option you choose, can have as many matches as possible
213 /// in a sub-round or mainly even matches in each sub-round.
214 /// Make sure to call the draw method first.
215 ///
216 /// # Example Single Tournament
217 /// ```
218 /// use social_tournament::{Round, SocialTournament, TournamentConfig, TableConfig};
219 /// use social_tournament::table::Table;
220 ///
221 /// let mut tournament = SocialTournament::new(TournamentConfig::Single {
222 /// number_of_players: 12,
223 /// number_of_rounds: 2,
224 /// table_config: TableConfig { available_tables: 10, distribution_option: None }
225 /// });
226 ///
227 /// tournament.draw().unwrap();
228 /// tournament.distribute().unwrap();
229 ///
230 /// let tables: Vec<Vec<Table>> = tournament.tables.unwrap();
231 /// /*
232 /// Creates:
233 /// Table { table_number: 0, occupied_number: 0 }
234 /// Table { table_number: 1, occupied_number: 0 }
235 /// Table { table_number: 2, occupied_number: 0 }
236 /// Table { table_number: 3, occupied_number: 0 }
237 /// Table { table_number: 0, occupied_number: 1 }
238 /// Table { table_number: 1, occupied_number: 1 }
239 /// --------------
240 /// Table { table_number: 0, occupied_number: 0 }
241 /// Table { table_number: 1, occupied_number: 0 }
242 /// Table { table_number: 2, occupied_number: 0 }
243 /// Table { table_number: 3, occupied_number: 0 }
244 /// Table { table_number: 0, occupied_number: 1 }
245 /// Table { table_number: 1, occupied_number: 1 }
246 /// --------------
247 /// */
248 /// ```
249 /// # Example Double Tournament
250 /// ```
251 /// use social_tournament::{Round, SocialTournament, TournamentConfig, TableConfig};
252 /// use social_tournament::table::Table;
253 ///
254 /// let mut tournament = SocialTournament::new(TournamentConfig::Double {
255 /// number_of_players: 24,
256 /// number_of_rounds: 2,
257 /// draw_option: None,
258 /// table_config: TableConfig { available_tables: 10, distribution_option: None }
259 /// });
260 ///
261 /// tournament.draw().unwrap();
262 /// tournament.distribute().unwrap();
263 ///
264 /// let tables: Vec<Vec<Table>> = tournament.tables.unwrap();
265 /// /*
266 /// Creates:
267 /// Table { table_number: 0, occupied_number: 0 }
268 /// Table { table_number: 1, occupied_number: 0 }
269 /// Table { table_number: 2, occupied_number: 0 }
270 /// Table { table_number: 3, occupied_number: 0 }
271 /// Table { table_number: 0, occupied_number: 1 }
272 /// Table { table_number: 1, occupied_number: 1 }
273 /// --------------
274 /// Table { table_number: 0, occupied_number: 0 }
275 /// Table { table_number: 1, occupied_number: 0 }
276 /// Table { table_number: 2, occupied_number: 0 }
277 /// Table { table_number: 3, occupied_number: 0 }
278 /// Table { table_number: 0, occupied_number: 1 }
279 /// Table { table_number: 1, occupied_number: 1 }
280 /// --------------
281 /// */
282 /// ```
283 ///
284 pub fn distribute(&mut self) -> Result<()> {
285 match self.rounds.clone() {
286 None => {
287 Err(DefaultError())
288 }
289 Some(rounds) => {
290 match self.config.clone() {
291 TournamentConfig::Single { table_config, .. } => {
292 self.tables = Some(distribute_tables(&rounds, table_config.available_tables, table_config.distribution_option))
293 }
294 TournamentConfig::Double { table_config, .. } => {
295 self.tables = Some(distribute_tables(&rounds, table_config.available_tables, table_config.distribution_option))
296 }
297 }
298 Ok(())
299 }
300 }
301 }
302
303 /// Public interface to generate route cards for a drawn tournament with distributed tables.
304 ///
305 /// Available [Language] are English and German. Make sure you have drawn and distributed before
306 /// calling this method.
307 ///
308 /// # Example Double Tournament
309 /// ```
310 /// use social_tournament::{Round, SocialTournament, TournamentConfig, TableConfig};
311 /// use social_tournament::table::Table;
312 /// use social_tournament::pdf::language::Language;
313 ///
314 /// let mut tournament = SocialTournament::new(TournamentConfig::Double {
315 /// number_of_players: 24,
316 /// number_of_rounds: 2,
317 /// draw_option: None,
318 /// table_config: TableConfig { available_tables: 10, distribution_option: None }
319 /// });
320 ///
321 /// tournament.draw().unwrap();
322 /// tournament.distribute().unwrap();
323 /// tournament.create_route_cards_pdf(Language::EN).unwrap();
324 ///
325 /// let pdf: Vec<u8> = tournament.pdf.unwrap();
326 pub fn create_route_cards_pdf(&mut self, language: Language) -> Result<()> {
327 match (self.rounds.clone(), self.tables.clone()) {
328 (Some(rounds), Some(tables)) => {
329 match self.config.clone() {
330 TournamentConfig::Single { .. } => {
331 //TODO
332 }
333 TournamentConfig::Double { number_of_players, draw_option, table_config, .. } => {
334 let draw_o = draw_option.unwrap_or(DrawOption::AllInAction);
335 self.pdf = Some(create_route_cards_for_double(&rounds, &tables, number_of_players, draw_o, table_config.available_tables, language))
336 }
337 }
338 Ok(())
339 }
340 _ => Err(DefaultError())
341 }
342 }
343}