Vereinsmeisterschaften  22aa7800eae54b428d40e835886cefe1fdefdfdf
This is a software that can be used to manage the internal competition of the swimming club Illertissen called "Vereinsmeisterschaften".
Loading...
Searching...
No Matches
RacesVariant.cs
1using System.Collections.ObjectModel;
2using System.Collections.Specialized;
3using System.ComponentModel;
4using CommunityToolkit.Mvvm.ComponentModel;
7
9{
13 public partial class RacesVariant : ObservableObject, IEquatable<RacesVariant>, ICloneable
14 {
15 #region Constructors / Destructor
16
17 private IWorkspaceService _workspaceService;
18
23 public RacesVariant(IWorkspaceService workspaceService = null)
24 {
25 Races = new ObservableCollection<Race>();
26 Races.CollectionChanged += Races_CollectionChanged;
27 _workspaceService = workspaceService;
28 if (_workspaceService != null) { _workspaceService.PropertyChanged += workspaceServicePropertyChangedEvent; }
29 }
30
36 public RacesVariant(List<Race> races, IWorkspaceService workspaceService = null)
37 {
38 Races = new ObservableCollection<Race>(races);
39 Races.CollectionChanged += Races_CollectionChanged;
40 _workspaceService = workspaceService;
41 if (_workspaceService != null) { _workspaceService.PropertyChanged += workspaceServicePropertyChangedEvent; }
42
43 Races_CollectionChanged(Races, null);
44 }
45
51 public RacesVariant(ObservableCollection<Race> races, IWorkspaceService workspaceService = null)
52 {
53 Races = races;
54 Races.CollectionChanged += Races_CollectionChanged;
55 _workspaceService = workspaceService;
56 if (_workspaceService != null) { _workspaceService.PropertyChanged += workspaceServicePropertyChangedEvent; }
57
58 Races_CollectionChanged(Races, null);
59 }
60
68 public RacesVariant(RacesVariant other, bool deepClone = true, bool deepCloneRaces = true, IWorkspaceService workspaceService = null) : this()
69 {
70 if (other == null) { return; }
71 if (deepClone)
72 {
73 // Create a deep copy of the list
74 Races = new ObservableCollection<Race>(other.Races.Select(item => new Race(item, deepCloneRaces)));
75 }
76 else
77 {
78 // Create a new list but keep the references to the <see cref="Race"/> objects
79 Races = new ObservableCollection<Race>(other.Races);
80 }
81
82 Races.CollectionChanged += Races_CollectionChanged;
83 _workspaceService = workspaceService ?? other._workspaceService;
84 if (_workspaceService != null) { _workspaceService.PropertyChanged += workspaceServicePropertyChangedEvent; }
85
86 VariantID = other.VariantID;
87 Races_CollectionChanged(Races, null);
88 }
89
94 {
95 if (_workspaceService != null) { _workspaceService.PropertyChanged -= workspaceServicePropertyChangedEvent; }
96 }
97
98 #endregion
99
100 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
101
102 #region Basic properties
103
107 public ObservableCollection<Race> Races { get; set; }
108
109 #endregion
110
111 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
112
113 #region Not assigned starts
114
115 private List<PersonStart> _notAssignedStarts;
119 [FileServiceIgnore]
120 public List<PersonStart> NotAssignedStarts
121 {
122 get => _notAssignedStarts;
123 set => SetProperty(ref _notAssignedStarts, value);
124 }
125
130 public void UpdateNotAssignedStarts(List<PersonStart> allStarts)
131 {
132 List<PersonStart> raceStarts = GetAllStarts();
133 if (allStarts == null)
134 {
135 NotAssignedStarts = new List<PersonStart>();
136 }
137 else if (raceStarts == null)
138 {
139 NotAssignedStarts = allStarts.Where(s => s.IsActive).ToList();
140 }
141 else
142 {
143 NotAssignedStarts = allStarts?.Except(raceStarts)?.Where(s => s.IsActive)?.ToList();
144 }
145 OnPropertyChanged(nameof(IsValid_AllRacesValid));
146 OnPropertyChanged(nameof(IsValid_AllStartsAssigned));
147 OnPropertyChanged(nameof(IsValid));
148 }
149
150 #endregion
151
152 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
153
154 #region Other properties
155
156 private int _variantID;
160 [FileServiceIgnore]
161 public int VariantID
162 {
163 get => _variantID;
164 set => SetProperty(ref _variantID, value);
165 }
166
167 private bool _isPersistent;
171 [FileServiceIgnore]
172 public bool IsPersistent
173 {
174 get => _isPersistent;
175 set
176 {
177 if (SetProperty(ref _isPersistent, value))
178 {
179 OnPropertyChanged(nameof(KeepWhileRacesCalculation));
180 }
181 }
182 }
183
184 private bool _keepWhileRacesCalculation;
188 [FileServiceIgnore]
190 {
191 get => _keepWhileRacesCalculation || IsPersistent;
192 set => SetProperty(ref _keepWhileRacesCalculation, value);
193 }
194
195 #endregion
196
197 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
198
199 #region Validation properties
200
204 [FileServiceIgnore]
205 public bool IsValid_AllRacesValid => Races?.All(r => r.IsValid) ?? true;
206
210 [FileServiceIgnore]
212
219
220 #endregion
221
222 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
223
224 #region Event handlers
225
226 private void workspaceServicePropertyChangedEvent(object sender, PropertyChangedEventArgs e)
227 {
228 if (e.PropertyName == nameof(IWorkspaceService.Settings))
229 {
231 }
232 }
233
238 {
239 OnPropertyChanged(nameof(Races));
240 foreach (Race race in Races)
241 {
242 race.Starts.CollectionChanged -= raceStartsCollectionChangedEventHandler;
243 race.Starts.CollectionChanged += raceStartsCollectionChangedEventHandler;
244 }
245 }
246
247 private void raceStartsCollectionChangedEventHandler(object sender, NotifyCollectionChangedEventArgs e)
248 {
250 OnPropertyChanged(nameof(Races));
251 OnPropertyChanged(nameof(IsValid_AllRacesValid));
252 OnPropertyChanged(nameof(IsValid_AllStartsAssigned));
253 OnPropertyChanged(nameof(IsValid));
254 }
255
256 private void Races_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
257 {
261 OnPropertyChanged(nameof(IsValid_AllRacesValid));
262 OnPropertyChanged(nameof(IsValid_AllStartsAssigned));
263 OnPropertyChanged(nameof(IsValid));
264 }
265
269 private void updateRaceIDs()
270 {
271 int id = 1;
272 foreach(Race race in Races)
273 {
274 race.RaceID = id;
275 id++;
276 }
277 }
278
279 #endregion
280
281 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
282
283 #region Helper methods
284
289 public List<PersonStart> GetAllStarts()
290 {
291 return Races.SelectMany(r => r.Starts).ToList();
292 }
293
294 #endregion
295
296 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
297
298 #region Score properties
299
303 public double Score
304 {
305 get
306 {
307 double weightSingleStarts = _workspaceService?.Settings?.GetSettingValue<double>(WorkspaceSettings.GROUP_RACE_CALCULATION, WorkspaceSettings.SETTING_RACE_CALCULATION_WEIGHT_SINGLE_STARTS) ?? 5;
308 double weightSameStyleSequence = _workspaceService?.Settings?.GetSettingValue<double>(WorkspaceSettings.GROUP_RACE_CALCULATION, WorkspaceSettings.SETTING_RACE_CALCULATION_WEIGHT_SAME_STYLE_SEQUENCE) ?? 5;
309 double weightPersonStartPauses = _workspaceService?.Settings?.GetSettingValue<double>(WorkspaceSettings.GROUP_RACE_CALCULATION, WorkspaceSettings.SETTING_RACE_CALCULATION_WEIGHT_PERSON_START_PAUSES) ?? 60;
310 double weightStyleOrder = _workspaceService?.Settings?.GetSettingValue<double>(WorkspaceSettings.GROUP_RACE_CALCULATION, WorkspaceSettings.SETTING_RACE_CALCULATION_WEIGHT_STYLE_ORDER) ?? 10;
311 double weightStartGenders = _workspaceService?.Settings?.GetSettingValue<double>(WorkspaceSettings.GROUP_RACE_CALCULATION, WorkspaceSettings.SETTING_RACE_CALCULATION_WEIGHT_START_GENDERS) ?? 5;
312 double weightFriendships = _workspaceService?.Settings?.GetSettingValue<double>(WorkspaceSettings.GROUP_RACE_CALCULATION, WorkspaceSettings.SETTING_RACE_CALCULATION_WEIGHT_FRIENDSHIP) ?? 5;
313 weightFriendships = AreFriendshipsDefined ? weightFriendships : 0;
314 double sumWeights = weightSingleStarts + weightSameStyleSequence + weightPersonStartPauses + weightStyleOrder + weightStartGenders + weightFriendships;
315
316 double score = 0;
317 score += ScoreSingleStarts * (weightSingleStarts / sumWeights);
318 score += ScoreSameStyleSequence * (weightSameStyleSequence / sumWeights);
319 score += ScorePersonStartPauses * (weightPersonStartPauses / sumWeights);
320 score += ScoreStyleOrder * (weightStyleOrder / sumWeights);
321 score += ScoreStartGenders * (weightStartGenders / sumWeights);
322 score += ScoreFriendships * (weightFriendships / sumWeights);
323
324 return LimitValue(score, 0, 100);
325 }
326 }
327
328 // ----------------------------------------------------------------------------------------------------------------------------------------------
329
333 [ObservableProperty]
334 private double _scoreSingleStarts;
335
339 [ObservableProperty]
341
345 [ObservableProperty]
347
351 [ObservableProperty]
352 private double _scoreStyleOrder;
353
357 [ObservableProperty]
358 private double _scoreStartGenders;
359
363 [ObservableProperty]
364 private double _scoreFriendships;
365
370 [ObservableProperty]
372
373 #endregion
374
375 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
376
377 #region Additional score properties
378
379 private List<Person> _personStartPausesSeverelyAffectedPersons;
385 {
386 get => _personStartPausesSeverelyAffectedPersons;
387 set
388 {
389 if(SetProperty(ref _personStartPausesSeverelyAffectedPersons, value))
390 {
391 OnPropertyChanged(nameof(AreStartPausesSeverelyAffectedPersonsAvailable));
392 }
393 }
394 }
395
400
401 #endregion
402
403 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
404
405 #region Score calculation methods
406
411 public double CalculateScore()
412 {
413 if (Races.Count == 0)
414 {
415 ScoreSingleStarts = 0;
416 ScoreSameStyleSequence = 0;
417 ScorePersonStartPauses = 0;
418 ScoreStyleOrder = 0;
419 ScoreStartGenders = 0;
420 ScoreFriendships = 0;
421 }
422 else
423 {
424 ScoreSingleStarts = EvaluateSingleStarts();
425 ScoreSameStyleSequence = EvaluateSameStyleSequence();
426 ScorePersonStartPauses = EvaluatePersonStartPauses();
427 ScoreStyleOrder = EvaluateStyleOrder();
428 ScoreStartGenders = EvaluateStartGenders();
429 ScoreFriendships = EvaluateFriendships();
430 }
431 OnPropertyChanged(nameof(Score));
432 return Score;
433 }
434
435 // ----------------------------------------------------------------------------------------------------------------------------------------------
436
442 private double EvaluateSingleStarts()
443 {
444 if (Races.Count == 0) return 0.0;
445
446 int singleStarts = Races.Count(r => r.Starts.Count == 1);
447 double ratio = (double)singleStarts / Races.Count;
448
449 // The less single starts, the better
450 return 100 * (1 - ratio);
451 }
452
453 // ----------------------------------------------------------------------------------------------------------------------------------------------
454
461 {
462 if (Races.Count <= 1) return 100.0; // Only one race = no evaluation needed
463
464 int matchingPairs = 0;
465 for (int i = 1; i < Races.Count; i++)
466 {
467 if (Races[i].Style == Races[i - 1].Style)
468 matchingPairs++;
469 }
470
471 return 100 * ((double)matchingPairs / (Races.Count - 1));
472 }
473
474 // ----------------------------------------------------------------------------------------------------------------------------------------------
475
483 {
484 double penalty = 0;
485 double maxPenalty = 0; // This is calculated dynamically
486
487 Dictionary<Person, int> lastRaceIndex = new();
488 Dictionary<Person, double> individualPenalties = new();
489 List<Person> severelyAffectedPersons = new List<Person>();
490
491 uint shortPausesThreshold = _workspaceService?.Settings?.GetSettingValue<uint>(WorkspaceSettings.GROUP_RACE_CALCULATION, WorkspaceSettings.SETTING_RACE_CALCULATION_SHORT_PAUSE_THRESHOLD) ?? 3;
492 int severelyAffectedPersonsCount = 0; // Number of persons with hard penalty
493
494 for (int i = 0; i < Races.Count; i++)
495 {
496 foreach (var personStart in Races[i].Starts)
497 {
498 // Only consider active starts
499 if (!personStart.IsActive) { continue; }
500
501 Person person = personStart.PersonObj;
502
503 if (lastRaceIndex.TryGetValue(person, out int lastIndex))
504 {
505 int distance = i - lastIndex;
506
507 double personPenalty = distance switch
508 {
509 1 => 100, // Extremly high penalty for directly consecutive starts
510 2 => 100, // Extremly high penalty for only one race pause
511 3 => 50, // Noticeable penalty for two races pause
512 4 => 30, // Noticeable penalty for three races pause
513 _ => Math.Max(0, 10 - distance) // Minimal penalty for 3+ races pause
514 };
515
516 // if the distance is below shortPausesThreshold races, this is regarded as severly affected
517 if (distance < shortPausesThreshold)
518 {
519 severelyAffectedPersonsCount++;
520 severelyAffectedPersons.Add(person);
521 }
522
523 // Save the highest penalty for a person
524 if (!individualPenalties.ContainsKey(person) || individualPenalties[person] < personPenalty)
525 {
526 individualPenalties[person] = personPenalty;
527
528 maxPenalty += 100; // Add the highest possible penalty
529 }
530 }
531
532 lastRaceIndex[person] = i;
533 }
534 }
535
536 PersonStartPausesSeverelyAffectedPersons = severelyAffectedPersons.Distinct().ToList();
537
538 // Return the worst score if there are severely affected persons
539 if (severelyAffectedPersonsCount > 0)
540 {
541 return 0;
542 }
543 else
544 {
545 // Sum up the hardest penalties per person
546 penalty = individualPenalties.Values.Sum();
547 double score = 100 - (penalty / maxPenalty * 100);
548 return LimitValue(score, 0, 100);
549 }
550 }
551
552 // ----------------------------------------------------------------------------------------------------------------------------------------------
553
559 private double EvaluateStyleOrder()
560 {
561 if (Races.Count == 0) return 100.0;
562
563 // lower numbers should start earlier
564 Dictionary<SwimmingStyles, int> StylePriorities = new()
565 {
566 { SwimmingStyles.Breaststroke, _workspaceService?.Settings?.GetSettingValue<int>(WorkspaceSettings.GROUP_RACE_CALCULATION, WorkspaceSettings.SETTING_RACE_CALCULATION_PRIORITY_STYLE_BREASTSTROKE) ?? 1 },
567 { SwimmingStyles.Freestyle, _workspaceService?.Settings?.GetSettingValue<int>(WorkspaceSettings.GROUP_RACE_CALCULATION, WorkspaceSettings.SETTING_RACE_CALCULATION_PRIORITY_STYLE_FREESTYLE) ?? 2 },
568 { SwimmingStyles.Backstroke, _workspaceService?.Settings?.GetSettingValue<int>(WorkspaceSettings.GROUP_RACE_CALCULATION, WorkspaceSettings.SETTING_RACE_CALCULATION_PRIORITY_STYLE_BACKSTROKE) ?? 3 },
569 { SwimmingStyles.Butterfly, _workspaceService?.Settings?.GetSettingValue<int>(WorkspaceSettings.GROUP_RACE_CALCULATION, WorkspaceSettings.SETTING_RACE_CALCULATION_PRIORITY_STYLE_BUTTERFLY) ?? 4 },
570 { SwimmingStyles.Medley, _workspaceService?.Settings?.GetSettingValue<int>(WorkspaceSettings.GROUP_RACE_CALCULATION, WorkspaceSettings.SETTING_RACE_CALCULATION_PRIORITY_STYLE_MEDLEY) ?? 5 },
571 { SwimmingStyles.WaterFlea, _workspaceService?.Settings?.GetSettingValue<int>(WorkspaceSettings.GROUP_RACE_CALCULATION, WorkspaceSettings.SETTING_RACE_CALCULATION_PRIORITY_STYLE_WATERFLEA) ?? 6 }
572 };
573
574 double penalty = 0;
575 foreach (var (race, index) in Races.Select((r, i) => (r, i)))
576 {
577 if (StylePriorities.TryGetValue(race.Style, out int priority))
578 {
579 int expectedMinIndex = Races.Count * priority / StylePriorities.Count;
580 if (index < expectedMinIndex)
581 {
582 penalty += (expectedMinIndex - index);
583 }
584 }
585 }
586
587 double maxPenalty = Races.Count * StylePriorities.Count;
588 return 100 - LimitValue(100 * (penalty / maxPenalty), 0, 100);
589 }
590
591 // ----------------------------------------------------------------------------------------------------------------------------------------------
592
598 private double EvaluateStartGenders()
599 {
600 if (Races.Count == 0) return 100.0;
601
602 int totalRaces = Races.Count;
603 int homogeneousCount = 0;
604
605 foreach (Race race in Races)
606 {
607 List<Genders> genders = race.Starts.Select(s => s.PersonObj.Gender).Distinct().ToList();
608 if (genders.Count == 1)
609 {
610 homogeneousCount++;
611 }
612 }
613
614 double score = 100.0 * homogeneousCount / totalRaces;
615 return LimitValue(score, 0, 100);
616 }
617
618 // ----------------------------------------------------------------------------------------------------------------------------------------------
619
625 public double EvaluateFriendships()
626 {
627 if (Races == null || Races.Count == 0) return 100.0;
628
629 double totalFriendScore = 0;
630 double maxPossibleScore = 0;
631 AreFriendshipsDefined = false;
632
633 foreach (Race race in Races)
634 {
635 List<Person> personsInRace = race.Starts.Select(s => s.PersonObj).ToList();
636
637 // Skip races where all persons have no friends
638 if (!personsInRace.Any(p => p.Friends != null && p.Friends.Count > 0)) continue;
639
640 double raceScore = 0;
641 double raceMax = 0;
642
643 foreach (Person p1 in personsInRace)
644 {
645 if (p1.Friends == null || p1.Friends.Count == 0 || !p1.IsActive) continue;
646
647 AreFriendshipsDefined = true;
648
649 // Only evaluate friends that could compete in the same style / distance
650 List<Person> potentialFriends = p1.Friends.Where(f =>
651 {
652 PersonStart friendStart = f.GetStartByStyle(race.Style);
653 return friendStart != null && friendStart.CompetitionObj != null && friendStart.CompetitionObj?.Distance == race.Distance;
654 }).ToList();
655
656 if (potentialFriends.Count == 0) continue;
657
658 int friendCountInRace = personsInRace.Count(p2 => potentialFriends.Contains(p2));
659
660 raceScore += friendCountInRace;
661 raceMax += potentialFriends.Count;
662 }
663
664 if (raceMax > 0)
665 {
666 totalFriendScore += raceScore / raceMax;
667 maxPossibleScore += 1;
668 }
669 }
670
671 // Neutral score if no friendships are defined
672 if (maxPossibleScore == 0) return 50.0;
673
674 double score = (totalFriendScore / maxPossibleScore) * 100.0;
675 return LimitValue(score, 0, 100);
676 }
677
678 // ----------------------------------------------------------------------------------------------------------------------------------------------
679
687 private static double LimitValue(double value, double min, double max)
688 {
689 return Math.Max(min, Math.Min(max, value));
690 }
691
692#endregion
693
694 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
695
696 #region Equality, HashCode, ToString, Clone
697
703 public override bool Equals(object obj)
704 => obj is RacesVariant r && r.VariantID == VariantID;
705
711 public bool Equals(RacesVariant other)
712 => Equals((object)other);
713
718 public override int GetHashCode()
719 => VariantID.GetHashCode();
720
725 public object Clone()
726 => new RacesVariant(this, true, true);
727
728 #endregion
729 }
730}
ushort Distance
Distance in meters for this competition (e.g.
Class describing a person.
Definition Person.cs:12
List< Person > Friends
Friends of the person.
Definition Person.cs:525
bool IsActive
Check if at least one of the starts in the Starts dictionary is active.
Definition Person.cs:291
Class describing a start of a person.
Definition PersonStart.cs:9
Competition CompetitionObj
Reference to the competition object to which the start belongs.
Class that represents a single race.
Definition Race.cs:12
int Distance
Distance for this Race.
Definition Race.cs:82
ObservableCollection< PersonStart > Starts
List with all starts of this Race
Definition Race.cs:90
SwimmingStyles Style
for this .
Definition Race.cs:76
double _scoreSameStyleSequence
Score regarding same styles in sequence.
override int GetHashCode()
Serves as the default hash function.
static double LimitValue(double value, double min, double max)
Limit the value to [min, max] (both inclusive)
RacesVariant(List< Race > races, IWorkspaceService workspaceService=null)
Constructor for a new RacesVariant (copy an Race collection).
override bool Equals(object obj)
Compare if two RacesVariant are equal.
bool IsPersistent
If true, this RacesVariant should be persisted (to a file).
void UpdateNotAssignedStarts(List< PersonStart > allStarts)
Update the list of not assigned PersonStart objects.
object Clone()
Create a new object that has the same property values than this one.
RacesVariant(ObservableCollection< Race > races, IWorkspaceService workspaceService=null)
Constructor for a new RacesVariant (copy an Race collection).
double _scorePersonStartPauses
Score regarding pauses between person starts.
double CalculateScore()
Recalculate all scores.
double _scoreStyleOrder
Score regarding the preferred order of the styles.
RacesVariant(IWorkspaceService workspaceService=null)
Constructor for a new RacesVariant (create an empty Race collection).
double EvaluateSameStyleSequence()
Score for same styles in sequence: The more consecutive races with the same style the better.
double EvaluatePersonStartPauses()
Score for person pauses: The more pause between the starts of a person the better This returns 0 if t...
double EvaluateStartGenders()
Score for start genders: Homogenous genders in starts are better than heterogeneous ones.
bool _areFriendshipsDefined
True when at least one friendship is defined between persons in this RacesVariant.
double EvaluateStyleOrder()
Score for style order: There is a preferred order for the styles (can be defined via the workspace se...
ObservableCollection< Race > Races
List with races.
List< Person > PersonStartPausesSeverelyAffectedPersons
List of persons that are severely affected by short pauses between their starts.
double _scoreSingleStarts
Score regarding single starts.
bool IsValid_AllStartsAssigned
True when there are no empty unassigned races.
double _scoreFriendships
Score regarding friendships between persons.
~RacesVariant()
Destructor of the RacesVariant class.
bool KeepWhileRacesCalculation
Keep this RacesVariant while calculating new variants.
void updateRaceIDs()
Reassign the IDs for all races in Races starting from 1.
List< PersonStart > NotAssignedStarts
List with not assigned objects (not part of any in Races)
RacesVariant(RacesVariant other, bool deepClone=true, bool deepCloneRaces=true, IWorkspaceService workspaceService=null)
Create a new object that has the same property values than this one.
bool Equals(RacesVariant other)
Indicates wheather the current object is equal to another object of the same type.
bool AreStartPausesSeverelyAffectedPersonsAvailable
True if the number of persons who are severely affected by start pauses is greater than 0.
int VariantID
Number for this RacesVariant
double EvaluateSingleStarts()
Score for single starts: The less races with only one start the better.
bool IsValid_AllRacesValid
True when all Races are valid.
void UpdateRaceStartsCollectionChangedEvent()
Update the internal event handlers for the Race.Starts collection changed events.
bool IsValid
This RacesVariant is consideres valid when:
double _scoreStartGenders
Score regarding the homogenity of genders in the starts.
List< PersonStart > GetAllStarts()
Get a list with all objects of this
double EvaluateFriendships()
Score for friendships: The more friends start together in the same race the better.
Interface for a service used to manage a workspace.
WorkspaceSettings Settings
Settings for the current workspace.
SwimmingStyles
Available swimming styles.