dilemma_tactix_lib/models/game_options.rs
1// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2// * Copyright (c) 2023-2024
3// *
4// * This project is dual-licensed under the MIT and Apache licenses.
5// *
6// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7// ** APACHE 2.0 LICENSE
8// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9// *
10// * Licensed under the Apache License, Version 2.0 (the "License");
11// * you may not use this file except in compliance with the License.
12// * You may obtain a copy of the License at
13// *
14// * http://www.apache.org/licenses/LICENSE-2.0
15// *
16// * Unless required by applicable law or agreed to in writing, software
17// * distributed under the License is distributed on an "AS IS" BASIS,
18// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19// * See the License for the specific language governing permissions and
20// * limitations under the License.
21// *
22// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23// * * MIT LICENSE
24// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
25// *
26// * Permission is hereby granted, free of charge, to any person obtaining a copy
27// * of this software and associated documentation files (the "Software"), to deal
28// * in the Software without restriction, including without limitation the rights
29// * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
30// * copies of the Software, and to permit persons to whom the Software is
31// * furnished to do so, subject to the following conditions:
32// *
33// * The above copyright notice and this permission notice shall be included in all
34// * copies or substantial portions of the Software.
35// *
36// * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
37// * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
38// * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
39// * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
40// * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
41// * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
42// * SOFTWARE.
43// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
44
45use std::fmt::Display;
46
47#[cfg(test)]
48use crate::RANDOM_SEED;
49use crate::{
50 ChoiceNameOptions,
51 GameOptionsBuilder,
52 NumberPair,
53};
54
55/// This is a struct that holds the options for a game.
56///
57/// This struct is used to encapsulate the parameters to be used for generating
58/// a single grid from the parameters that may be related to a tournament or a
59/// series of grids.
60///
61/// The two parameters are:
62///
63/// * `min_value` - The minimum value for that can be assigned to a choice.
64/// * `max_value` - The maximum value for that can be assigned to a choice.
65///
66/// # Example
67///
68/// ## Explicit Options
69///
70/// ```
71/// use dilemma_tactix_lib::GameOptions;
72///
73/// let game_options = GameOptions::new(1, 10);
74/// ```
75///
76/// ## Default Options
77///
78/// ```
79/// use dilemma_tactix_lib::GameOptions;
80///
81/// let game_options = GameOptions::default();
82/// ```
83///
84/// ## Builder
85///
86/// ```
87/// use dilemma_tactix_lib::GameOptions;
88///
89/// let game_options = GameOptions::builder("customized").build();
90/// ```
91///
92/// # Notes
93///
94/// The `GameOptions` struct implements the `Default` trait, and can be created
95/// with the `default()` method.
96///
97/// # See Also
98///
99/// * [`GameOptions::new()`](#method.new)
100/// * [`GameOptions::default()`](#method.default)
101/// * [`GameOptions::builder()`](#method.builder)
102#[derive(Debug, Clone, Copy, PartialEq, Eq)]
103pub struct GameOptions {
104 /// The label for the first choice that can be made
105 pub choice_atlantis: &'static str,
106 /// The label for the second choice that can be made
107 pub choice_olympus: &'static str,
108 /// Score for Aleph-Atlantis and Beth-Atlantis
109 pub atlantis_atlantis: NumberPair,
110 /// Score for Aleph-Atlantis and Beth-Olympus
111 pub atlantis_olympus: NumberPair,
112 /// Score for Aleph-Olympus and Beth-Atlantis
113 pub olympus_atlantis: NumberPair,
114 /// Score for Aleph-Olympus and Beth-Olympus
115 pub olympus_olympus: NumberPair,
116}
117
118impl GameOptions {
119 /// Creates a new `GameOptions` struct.
120 ///
121 /// This function creates a new `GameOptions` struct with the given
122 /// parameters.
123 ///
124 /// In the implementation, the new struct instance instantiates the
125 /// `ChoiceNameOptions` struct, and then uses it to get a random pair of
126 /// choices. It then uses the given `min_value` and `max_value` to generate
127 /// random scores for the four possible combinations of outcomes.
128 ///
129 /// # Arguments
130 ///
131 /// * `min_value` - The minimum score for that can be assigned to a choice.
132 /// * `max_value` - The maximum score for that can be assigned to a choice.
133 ///
134 /// # Panics
135 ///
136 /// Panics if `min_value` is greater than `max_value`.
137 ///
138 /// # See Also
139 ///
140 /// * [`GameOptions::default()`](#method.default)
141 #[must_use]
142 pub fn new(min_value: u32, max_value: u32) -> Self {
143 #[cfg(test)]
144 let (choice_atlantis, choice_olympus) =
145 ChoiceNameOptions::get_random_pair_seeded(RANDOM_SEED.0);
146
147 #[cfg(test)]
148 let (atlantis_atlantis, atlantis_olympus, olympus_atlantis, olympus_olympus) = (
149 NumberPair::random_seeded(min_value, max_value, RANDOM_SEED.0),
150 NumberPair::random_seeded(min_value, max_value, RANDOM_SEED.1),
151 NumberPair::random_seeded(min_value, max_value, RANDOM_SEED.2),
152 NumberPair::random_seeded(min_value, max_value, RANDOM_SEED.3),
153 );
154
155 #[cfg(not(test))]
156 let (choice_atlantis, choice_olympus) = ChoiceNameOptions::get_random_pair();
157
158 #[cfg(not(test))]
159 let (atlantis_atlantis, atlantis_olympus, olympus_atlantis, olympus_olympus) = (
160 NumberPair::random(min_value, max_value),
161 NumberPair::random(min_value, max_value),
162 NumberPair::random(min_value, max_value),
163 NumberPair::random(min_value, max_value),
164 );
165
166 Self {
167 choice_atlantis,
168 choice_olympus,
169 atlantis_atlantis,
170 atlantis_olympus,
171 olympus_atlantis,
172 olympus_olympus,
173 }
174 }
175
176 /// Returns the value of `choice_atlantis`.
177 ///
178 /// This function returns the value of `choice_atlantis`.
179 ///
180 /// # Returns
181 ///
182 /// The value of `choice_atlantis`.
183 ///
184 /// # See Also
185 ///
186 /// * [`GameOptions::min_value()`](#method.min_value)
187 /// * [`GameOptions::max_value()`](#method.max_value)
188 /// * [`GameOptions::choice_olympus()`](#method.choice_olympus)
189 #[must_use]
190 pub const fn choice_atlantis(&self) -> &str {
191 self.choice_atlantis
192 }
193
194 /// Returns the value of `choice_olympus`.
195 ///
196 /// This function returns the value of `choice_olympus`.
197 ///
198 /// # Returns
199 ///
200 /// The value of `choice_olympus`.
201 ///
202 /// # See Also
203 ///
204 /// * [`GameOptions::min_value()`](#method.min_value)
205 /// * [`GameOptions::max_value()`](#method.max_value)
206 /// * [`GameOptions::choice_atlantis()`](#method.choice_atlantis)
207 #[must_use]
208 pub const fn choice_olympus(&self) -> &str {
209 self.choice_olympus
210 }
211
212 /// Returns the value of `atlantis_atlantis`.
213 ///
214 /// This function returns the value of `atlantis_atlantis`, which is the
215 /// `NumberPair` containing the scores for the case when both Player Aleph
216 /// and Player Beth choose the Atlantis strategy.
217 ///
218 /// # Returns
219 ///
220 /// The value of `atlantis_atlantis` as a
221 /// [`NumberPair`](#struct.NumberPair).
222 ///
223 /// # See Also
224 ///
225 /// * [`GameOptions::atlantis_olympus()`](#method.atlantis_olympus)
226 /// * [`GameOptions::olympus_atlantis()`](#method.olympus_atlantis)
227 /// * [`GameOptions::olympus_olympus()`](#method.olympus_olympus)
228 #[must_use]
229 pub const fn atlantis_atlantis(&self) -> NumberPair {
230 self.atlantis_atlantis
231 }
232
233 /// Returns the value of `atlantis_olympus`.
234 ///
235 /// This function returns the value of `atlantis_olympus`, which is the
236 /// `NumberPair` containing the scores for the case when Player Aleph
237 /// chooses the Atlantis strategy and Player Beth chooses the Olympus
238 /// strategy.
239 ///
240 /// # Returns
241 ///
242 /// The value of `atlantis_olympus` as a [`NumberPair`](#struct.NumberPair).
243 ///
244 /// # See Also
245 ///
246 /// * [`GameOptions::atlantis_atlantis()`](#method.atlantis_atlantis)
247 /// * [`GameOptions::olympus_atlantis()`](#method.olympus_atlantis)
248 /// * [`GameOptions::olympus_olympus()`](#method.olympus_olympus)
249 #[must_use]
250 pub const fn atlantis_olympus(&self) -> NumberPair {
251 self.atlantis_olympus
252 }
253
254 /// Returns the value of `olympus_atlantis`.
255 ///
256 /// This function returns the value of `olympus_atlantis`, which is the
257 /// `NumberPair` containing the scores for the case when Player Aleph
258 /// chooses the Olympus strategy and Player Beth chooses the Atlantis
259 /// strategy.
260 ///
261 /// # Returns
262 ///
263 /// The value of `olympus_atlantis` as a [`NumberPair`](#struct.NumberPair).
264 ///
265 /// # See Also
266 ///
267 /// * [`GameOptions::atlantis_atlantis()`](#method.atlantis_atlantis)
268 /// * [`GameOptions::atlantis_olympus()`](#method.atlantis_olympus)
269 /// * [`GameOptions::olympus_olympus()`](#method.olympus_olympus)
270 #[must_use]
271 pub const fn olympus_atlantis(&self) -> NumberPair {
272 self.olympus_atlantis
273 }
274
275 /// Returns the value of `olympus_olympus`.
276 ///
277 /// This function returns the value of `olympus_olympus`, which is the
278 /// `NumberPair` containing the scores for the case when both Player Aleph
279 /// and Player Beth choose the Olympus strategy.
280 ///
281 /// # Returns
282 ///
283 /// The value of `olympus_olympus` as a [`NumberPair`](#struct.NumberPair).
284 ///
285 /// # See Also
286 ///
287 /// * [`GameOptions::atlantis_atlantis()`](#method.atlantis_atlantis)
288 /// * [`GameOptions::atlantis_olympus()`](#method.atlantis_olympus)
289 /// * [`GameOptions::olympus_atlantis()`](#method.olympus_atlantis)
290 #[must_use]
291 pub const fn olympus_olympus(&self) -> NumberPair {
292 self.olympus_olympus
293 }
294
295 /// Create a builder for a `GameOptions` struct.
296 ///
297 /// This function creates a builder for a `GameOptions` struct which allows
298 /// for step-by-step building of individual game options objects, bypassing
299 /// random generation of values if desired.
300 ///
301 /// # Arguments
302 ///
303 /// * `builder_type` - The type of builder to create. Valid values are
304 /// `randomized`, `seeded`, and `customized`.
305 ///
306 /// # Example
307 ///
308 /// ```
309 /// use dilemma_tactix_lib::GameOptions;
310 ///
311 /// let game_options = GameOptions::builder("customized");
312 /// ```
313 ///
314 /// # Returns
315 ///
316 /// A new `GameOptionsBuilder` struct.
317 ///
318 /// # Notes
319 ///
320 /// Unlike the `default` method. a `default` builder is guaranteed to
321 /// be the same each time it is called.
322 ///
323 /// # See Also
324 ///
325 /// * [`GameOptionsBuilder`](#struct.GameOptionsBuilder)
326 /// * [`GameOptions::new()`](#method.new)
327 /// * [`GameOptions::default()`](#method.default)
328 /// * [`GameOptionsBuilder::build()`](#method.build)
329 #[must_use]
330 pub fn builder(builder_type: &str) -> GameOptionsBuilder {
331 match builder_type {
332 "seeded" | "Seeded" => {
333 GameOptionsBuilder::new(super::game_option_builder::GameOptionsBuilderTypes::Seeded)
334 }
335 "customized" | "Customized" => GameOptionsBuilder::new(
336 super::game_option_builder::GameOptionsBuilderTypes::Customized,
337 ),
338 _ => GameOptionsBuilder::new(
339 super::game_option_builder::GameOptionsBuilderTypes::Randomized,
340 ),
341 }
342 }
343}
344
345impl Default for GameOptions {
346 /// Creates a new `GameOptions` struct with default values.
347 ///
348 /// This function creates a new `GameOptions` struct with default values.
349 ///
350 /// The default values are:
351 ///
352 /// * `min_value` - 1
353 /// * `max_value` - 10
354 /// * `choice_atlantis` - "cooperate"
355 /// * `choice_olympus` - "defect"
356 ///
357 /// # Returns
358 ///
359 /// A new `GameOptions` struct with default values for the parameters.
360 ///
361 /// # Notes
362 ///
363 /// Unlike the default options in the `builder()` method, the default
364 /// options here are guaranteed to generate a new random grid each time.
365 ///
366 /// # See Also
367 ///
368 /// * [`GameOptions::new()`](#method.new)
369 /// * [`GameOptions::builder()`](#method.builder)
370
371 fn default() -> Self {
372 Self::new(1, 10)
373 }
374}
375
376impl Display for GameOptions {
377 /// Implements the Display trait for `GameOptions`.
378 ///
379 /// This function implements the Display trait for `GameOptions`.
380
381 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
382 write!(
383 f,
384 "choice_atlantis: {}, choice_olympus: {}, atlantis_atlantis: {}, atlantis_olympus: \
385 {}, olympus_atlantis: {}, olympus_olympus: {}",
386 self.choice_atlantis,
387 self.choice_olympus,
388 self.atlantis_atlantis,
389 self.atlantis_olympus,
390 self.olympus_atlantis,
391 self.olympus_olympus
392 )
393 }
394}
395
396#[cfg(test)]
397mod tests {
398
399 use rstest::{
400 fixture,
401 rstest,
402 };
403
404 use super::*;
405
406 #[fixture]
407 fn choice_atlantis_options() -> [&'static str; 17] {
408 ChoiceNameOptions::choice_atlantis_options()
409 }
410
411 #[fixture]
412 fn choice_olympus_options() -> [&'static str; 17] {
413 ChoiceNameOptions::choice_olympus_options()
414 }
415
416 #[rstest]
417 fn test_game_options_default(
418 choice_atlantis_options: [&'static str; 17],
419 choice_olympus_options: [&'static str; 17],
420 ) {
421 let game_options = GameOptions::default();
422
423 assert_eq!(game_options.atlantis_atlantis(), NumberPair::new(6, 9));
424
425 assert_eq!(game_options.atlantis_olympus(), NumberPair::new(3, 8));
426
427 assert_eq!(game_options.olympus_atlantis(), NumberPair::new(6, 7));
428
429 assert_eq!(game_options.olympus_olympus(), NumberPair::new(3, 6));
430
431 assert_eq!(game_options.choice_atlantis(), "discrete");
432
433 assert_eq!(game_options.choice_olympus(), "continuous");
434
435 assert!(choice_atlantis_options.contains(&game_options.choice_atlantis()));
436
437 assert!(choice_olympus_options.contains(&game_options.choice_olympus()));
438 }
439
440 #[test]
441 fn test_game_options_new() {
442 let game_options = GameOptions::new(1, 10);
443
444 assert_eq!(game_options.atlantis_atlantis(), NumberPair::new(6, 9));
445
446 assert_eq!(game_options.atlantis_olympus(), NumberPair::new(3, 8));
447
448 assert_eq!(game_options.olympus_atlantis(), NumberPair::new(6, 7));
449
450 assert_eq!(game_options.olympus_olympus(), NumberPair::new(3, 6));
451
452 assert_eq!(game_options.choice_atlantis(), "discrete");
453
454 assert_eq!(game_options.choice_olympus(), "continuous");
455 }
456
457 #[test]
458 fn test_game_options_display() {
459 let game_options = GameOptions::new(1, 10);
460
461 assert_eq!(
462 format!("{}", game_options),
463 "choice_atlantis: discrete, choice_olympus: continuous, atlantis_atlantis: (6, 9), \
464 atlantis_olympus: (3, 8), olympus_atlantis: (6, 7), olympus_olympus: (3, 6)"
465 );
466 }
467
468 #[test]
469 fn test_builder_randomized() {
470 let builder = GameOptions::builder("randomized");
471
472 assert!(builder.choice_atlantis.is_none());
473
474 assert!(builder.choice_olympus.is_none());
475
476 assert!(builder.atlantis_atlantis.is_none());
477
478 assert!(builder.atlantis_olympus.is_none());
479
480 assert!(builder.olympus_atlantis.is_none());
481
482 assert!(builder.olympus_olympus.is_none());
483 }
484
485 #[test]
486 fn test_builder_seeded() {
487 let builder = GameOptions::builder("seeded");
488
489 assert!(builder.choice_atlantis.is_none());
490
491 assert!(builder.choice_olympus.is_none());
492
493 assert!(builder.atlantis_atlantis.is_none());
494
495 assert!(builder.atlantis_olympus.is_none());
496
497 assert!(builder.olympus_atlantis.is_none());
498
499 assert!(builder.olympus_olympus.is_none());
500 }
501
502 #[test]
503 fn test_builder_customized() {
504 let builder = GameOptions::builder("customized");
505
506 assert!(builder.choice_atlantis.is_none());
507
508 assert!(builder.choice_olympus.is_none());
509
510 assert!(builder.atlantis_atlantis.is_none());
511
512 assert!(builder.atlantis_olympus.is_none());
513
514 assert!(builder.olympus_atlantis.is_none());
515
516 assert!(builder.olympus_olympus.is_none());
517 }
518}