You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
PadelClub/PadelClub/Manager/PadelRule.swift

1398 lines
38 KiB

//
// PadelRule.swift
// Padel Tournament
//
// Created by razmig on 27/02/2023.
//
import Foundation
enum RankSource: Hashable {
case national
case ligue
case club(assimilation: Bool)
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .national:
return "Classement National"
case .ligue:
return "Classement Ligue"
case .club:
return "Classement Club"
}
}
}
struct TournamentBuild: Hashable, Codable, Identifiable {
var id: String { identifier }
let category: TournamentCategory
let level: TournamentLevel
let age: FederalTournamentAge
var japIdentifier: Int? = nil
var japFirstName: String? = nil
var japLastName: String? = nil
var identifier: String {
level.localizedLabel()+":"+category.localizedLabel()+":"+age.localizedLabel()
}
var computedLabel: String {
if age == .senior { return localizedLabel() }
return localizedLabel() + " " + localizedAge
}
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
level.localizedLabel() + category.localizedLabel(.short)
}
var localizedTitle: String {
level.localizedLabel() + " " + category.localizedLabel()
}
var localizedAge: String {
age.tournamentDescriptionLabel
}
}
extension TournamentBuild {
init?(category: String, level: String, age: FederalTournamentAge = .senior) {
guard let levelFound = TournamentLevel.allCases.first(where: { $0.localizedLabel() == level }) else { return nil }
var c = category
if c.hasPrefix("ME") {
c = "H"
}
if c.hasPrefix("F") {
c = "D"
}
guard let categoryFound = TournamentCategory.allCases.first(where: { c.canonicalVersion.hasPrefix($0.buildLabel.canonicalVersion) }) else { return nil }
self.level = levelFound
self.category = categoryFound
self.age = age
}
}
enum FederalTournamentType: String, Hashable, Codable, CaseIterable, Identifiable {
case tournoi = "P"
case championnatParEquipe = "S"
case championnatParPaire = "L"
var id: String { self.rawValue }
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .tournoi:
return "Tournois"
case .championnatParEquipe:
return "Championnats par équipes"
case .championnatParPaire:
return "Championnats par paires"
}
}
}
enum TournamentDifficulty {
case rankS
case rankA
case rankB
case rankC
init?(gameDifference: Double) {
switch gameDifference {
case ..<3:
self = .rankS
case ..<5:
self = .rankA
case ..<7:
self = .rankB
case 7...:
self = .rankC
default:
return nil
}
}
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .rankS:
return "S"
case .rankA:
return "A"
case .rankB:
return "B"
case .rankC:
return "C"
}
}
var backgroundColor: String {
switch self {
case .rankS:
return "#d4af37"
case .rankA:
return "#c0c0c0"
case .rankB:
return "#cd7f32"
case .rankC:
return "#DCC2E0"
}
}
}
enum FederalTournamentAge: Int, Hashable, Codable, CaseIterable, Identifiable {
case a11_12 = 120
case a13_14 = 140
case a15_16 = 160
case a17_18 = 180
case senior = 200
case a45 = 450
case a55 = 550
static func mostRecent(tournaments: [Tournament] = []) -> Self {
.senior
// return tournaments.first?.federalTournamentAge ?? .a11_12
}
static func mostUsed(tournaments: [Tournament] = []) -> Self {
// let countedSet = NSCountedSet(array: tournaments.map { $0.federalTournamentAge })
// let mostFrequent = countedSet.max { countedSet.count(for: $0) < countedSet.count(for: $1) }
// if mostFrequent != nil {
// return mostFrequent as! FederalTournamentAge
// } else {
// return mostRecent(tournaments: tournaments)
// }
.senior
}
var id: Int { self.rawValue }
var order: Int {
switch self {
case .a11_12:
return 6
case .a13_14:
return 5
case .a15_16:
return 4
case .a17_18:
return 3
case .senior:
return 0
case .a45:
return 1
case .a55:
return 2
}
}
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .a11_12:
return "11/12 ans"
case .a13_14:
return "13/14 ans"
case .a15_16:
return "15/16 ans"
case .a17_18:
return "17/18 ans"
case .senior:
return "Senior"
case .a45:
return "45 ans"
case .a55:
return "55 ans"
}
}
var tournamentDescriptionLabel: String {
switch self {
case .senior:
return ""
case .a45, .a55:
return "+" + localizedLabel()
default:
return localizedLabel()
}
}
}
enum TournamentLevel: Int, Hashable, Codable, CaseIterable, Identifiable {
case p25 = 25
case p100 = 100
case p250 = 250
case p500 = 500
case p1000 = 1000
case p1500 = 1500
case p2000 = 2000
static func mostRecent(tournaments: [Tournament] = []) -> Self {
//return tournaments.first?.tournamentLevel ?? .p25
.p100
}
static func mostUsed(tournaments: [Tournament] = []) -> Self {
// let countedSet = NSCountedSet(array: tournaments.map { $0.tournamentLevel })
// let mostFrequent = countedSet.max { countedSet.count(for: $0) < countedSet.count(for: $1) }
// if mostFrequent != nil {
// return mostFrequent as! TournamentLevel
// } else {
// return mostRecent(tournaments: tournaments)
// }
.p100
}
var id: Int { self.rawValue }
func maximumDuration() -> Double {
switch self {
case .p25:
return 0.5
default:
return 3
}
}
func minimumPlayerRank(category: TournamentCategory, ageCategory: FederalTournamentAge) -> Int {
switch self {
case .p25:
switch ageCategory {
case .senior:
return category == .men ? 10000 : 1000
default:
return 0
}
case .p100:
switch ageCategory {
case .senior:
return category == .men ? 2000 : 300
default:
return 0
}
case .p250:
switch ageCategory {
case .senior:
return category == .men ? 500 : 100
default:
return 0
}
default:
return 0
}
}
func federalFormatForGroupStage() -> MatchFormat {
federalFormatForBracketRound(5)
}
func federalFormatForBracketRound(_ roundIndex: Int) -> MatchFormat {
switch self {
case .p25:
return .superTie
case .p100:
return .nineGamesDecisivePoint
case .p250:
if roundIndex == 0 { //finale
return .twoSetsDecisivePointSuperTie
} else {
return .nineGamesDecisivePoint
}
case .p500:
if roundIndex == 0 { //finale
return .twoSetsDecisivePoint
} else if roundIndex == 1 { //demi-finale
return .twoSetsDecisivePointSuperTie
} else {
return .nineGamesDecisivePoint
}
case .p1000, .p1500:
if roundIndex <= 1 { //demi / finale
return .twoSetsDecisivePoint
} else {
return .twoSetsDecisivePointSuperTie
}
default:
return .twoSetsDecisivePoint
}
}
func decisivePointRequired(ageCategory: FederalTournamentAge) -> Bool {
switch ageCategory {
case .a11_12, .a13_14, .a15_16, .a17_18:
return true
default:
return false
}
}
func federalFormatForLoserBracketRound(_ roundIndex: Int) -> MatchFormat {
switch self {
case .p25:
return .superTie
case .p100, .p250, .p500:
return .nineGamesDecisivePoint
default:
if roundIndex <= 1 { //petite finale
return .twoSetsDecisivePointSuperTie
} else {
return .nineGamesDecisivePoint
}
}
}
var defaultTeamSortingType: TeamSortingType {
switch self {
case .p25, .p100, .p250:
return .inscriptionDate
default:
return .rank
}
}
var order: Int {
switch self {
case .p25:
return 6
case .p100:
return 5
case .p250:
return 4
case .p500:
return 3
case .p1000:
return 2
case .p1500:
return 1
case .p2000:
return 0
}
}
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
return String(describing: self).capitalized
}
var coachingIsAuthorized: Bool {
switch self {
case .p500, .p1000, .p1500, .p2000:
return true
default:
return false
}
}
func points(for rank: Int, count: Int) -> Int {
let points = points(for: count)
if rank >= points.count {
return points.last!
} else if rank == 0 {
return Int(self.rawValue)
} else {
return points[rank-1]
}
}
func allPoints(for count: Int) -> [Int] {
[Int(self.rawValue)] + points(for: count)
}
func points(for count: Int) -> [Int] {
switch self {
case .p25:
switch count {
case 9...12:
return [17, 13, 11, 9, 7, 5, 4, 3, 2, 1]
case 13...16:
return [18,16,15,14,13,12,11,10,9,7,5,4,3,2, 1]
case 17...20:
return [20,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2, 1]
case 21...24:
return [20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2, 1]
case 25...28:
return [21,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2, 1]
case _ where count > 28:
return [23,21,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2, 1]
default:
return [15, 12, 9, 6, 4, 2, 1]
}
case .p100:
switch count {
case 9...12:
return [65,55, 50, 35, 25, 20, 15, 10, 5, 3, 1]
case 13...16:
return [70, 60, 55, 45, 40, 35, 30, 25, 21, 18, 15, 10, 5, 3, 1]
case 17...20:
return [75,65,60,55,50,45,40,35,30,25,23,20,18,15,12,10,5,3, 1]
case 21...24:
return [75,70,65,60,55,50,47,43,40,37,33,30,28,25,23,20,18,15,12,10,5,3, 1]
case 25...28:
return [80,75,70,65,60,55,53,50,48,45,43,40,38,35,33,30,28,25,23,20,18,15,12,10,5,3, 1]
case _ where count > 28:
return [80,75,72,70,65,63,60,58,55,53,50,48,45,43,40,38,35,33,30,28,25,23,20,18,15,12,10,8,5,3, 1]
default:
return [60,50,40,25,10,5]
}
case .p250:
switch count {
case 9...12:
return [163,138,125,88,63,50,38,25,13,8,3]
case 13...16:
return [175,150,138,113,100,88,75,63,53,45,38,25,13,8,3]
case 17...20:
return [188,163,150,138,123,113,100,88,75,63,58,50,45,38,30,25,13,8,3]
case 21...24:
return [188,175,163,150,138,125,118,108,100,93,83,75,70,63,58,50,45,38,30,25,13,8,3]
case 25...28:
return [200,188,175,163,150,138,133,125,120,113,108,100,95,88,83,75,70,63,58,50,45,38,30,25,13,8,3]
case _ where count > 28:
return [200,188,180,175,163,158,150,145,138,133,125,120,113,108,100,95,88,83,75,70,63,58,50,45,38,30,25,20,13,8,3]
default:
return [150,125,100,63,25,13,3]
}
case .p500:
switch count {
case 9...12:
return [325,275,250,175,125,100,75,50,25,15,5]
case 13...16:
return [350,300,275,225,200,175,150,125,105,90,75,50,25,15,5]
case 17...20:
return [375,325,300,275,250,225,200,175,150,125,115,100,90,75,60,50,25,15,5]
case 21...24:
return [375,350,325,300,275,250,235,215,200,185,165,150,140,125,115,100,80,75,60,50,25,15,5]
case 25...28:
return [400,375,350,325,300,275,265,250,240,225,215,200,190,175,165,150,140,125,115,100,80,75,60,50,25,15,5]
case _ where count > 28:
return [400,375,360,350,325,315,300,290,275,265,250,240,225,215,200,190,175,165,150,140,125,115,100,80,75,60,50,40,25,15,5]
default:
return [300,250,200,125,50,25,5]
}
case .p1000:
switch count {
case 9...12:
return [650,550, 500, 350, 250, 200, 150, 100, 50, 30, 10]
case 13...16:
return [700, 600, 550, 450, 400, 350, 300, 250, 210, 180, 150, 100, 50, 30, 10]
case 17...20:
return [750,650,600,550,500,450,400,350,300,250,230,200,180,150,120,100,50,30, 10]
case 21...24:
return [750,700,650,600,550,500,470,430,400,370,330,300,280,250,230,200,180,150,120,100,50,30, 10]
case 25...28:
return [800,750,700,650,600,550,530,500,480,450,430,400,380,350,330,300,280,250,230,200,180,150,120,100,50,30, 10]
case _ where count > 28:
return [800,750,720,700,650,630,600,580,550,530,500,480,450,430,400,380,350,330,300,280,250,230,200,180,150,120,100,80,50,30, 10]
default:
return [600,500,400,250,100,50]
}
case .p1500:
switch count {
case 21...24:
return [1125,1050,975,900,825,750,705,645,600,555,495,450,420,375,345,300,270,225,180,150,75,45,15]
case 25...28:
return [1200,1125,1050,975,900,825,795,750,720,675,645,600,570,525,495,450,420,375,345,300,270,225,180,150,75,45,15]
case _ where count > 28:
return [1200,1125,1080,1050,975,945,900,870,825,795,750,720,675,645,600,570,525,495,450,420,375,345,300,270,225,180,150,120,75,45,15]
default:
return [1125,975,900,825,750,675,600,525,450,375,345,300,270,225,180,150,75,45,15]
}
case .p2000:
return [1600,1440,1300,1180,1120,1060,1000,880,840,800,760,720,680,640,600,540,520,500,480,460,440,420,400,260,200,40]
}
}
var ranges: [PlayersCountRange] {
switch self {
case .p1500:
return [.N16, .N24, .N28, .N32]
case .p2000:
return [.N32]
default:
return PlayersCountRange.allCases
}
}
enum NewBallSystem {
case perField
case perMatch(fromRound: Int?)
func localizedLabel(loserBracket: Bool = false) -> String {
switch self {
case .perField:
return "3 / piste"
case .perMatch(let fromRound):
if fromRound != nil {
if loserBracket {
return "3 / match pour les perdants des \(RoundLabel.shortLabels[fromRound!].lowercased())s"
} else {
return "3 / match à partir des \(RoundLabel.shortLabels[fromRound!].lowercased())s"
}
} else {
return "3 / match"
}
}
}
}
func minimumFormatFinalTableAndQualifier(roundIndex: Int) -> MatchFormat? {
switch self {
case .p25:
return nil
case .p100:
return .nineGamesDecisivePoint
case .p250:
if roundIndex == 0 { //final
return .twoSetsDecisivePointSuperTie
} else {
return .nineGamesDecisivePoint
}
case .p500:
if roundIndex == 0 { //final
return .twoSetsDecisivePoint
} else if roundIndex == 1 { //demi
return .twoSetsDecisivePointSuperTie
} else {
return .nineGamesDecisivePoint
}
case .p1000, .p1500, .p2000:
if roundIndex == 3 { //16eme
return .twoSetsDecisivePoint
} else {
return .twoSetsDecisivePointSuperTie
}
}
}
func minimumFormatLoserBracket(roundIndex: Int) -> MatchFormat? {
switch self {
case .p25:
return nil
case .p100, .p250, .p500:
return .nineGamesDecisivePoint
case .p1000, .p1500, .p2000:
if roundIndex == 1 { //demi
return .twoSetsDecisivePoint
} else {
return .nineGamesDecisivePoint
}
}
}
func newBallsFinalTable() -> NewBallSystem? {
switch self {
case .p25, .p100:
return .perField
case .p250:
return .perMatch(fromRound: 1) //demi
case .p500:
return .perMatch(fromRound: 2) //quart
case .p1000, .p1500, .p2000:
return .perMatch(fromRound: nil)
}
}
func newBallsLoserBracket() -> NewBallSystem? {
switch self {
case .p25, .p100:
return nil
case .p250:
return .perMatch(fromRound: 1) //demi
case .p500, .p1000, .p1500, .p2000:
return .perMatch(fromRound: 2) //quart
}
}
}
enum TournamentCategory: Int, Hashable, Codable, CaseIterable, Identifiable {
case men
case women
case mix
var localizedPlayerLabel: String {
switch self {
case .women:
return "joueuse"
default:
return "joueur"
}
}
static func mostRecent(tournaments: [Tournament] = []) -> Self {
//return tournaments.first?.tournamentCategory ?? .mix
.men
}
static func mostUsed(tournaments: [Tournament] = []) -> Self {
// let countedSet = NSCountedSet(array: tournaments.map { $0.tournamentCategory })
// let mostFrequent = countedSet.max { countedSet.count(for: $0) < countedSet.count(for: $1) }
// if mostFrequent != nil {
// return mostFrequent as! TournamentCategory
// } else {
// return mostRecent(tournaments: tournaments)
// }
.men
}
var next: TournamentCategory {
switch self {
case .men:
return .women
case .women:
return .mix
case .mix:
return .men
}
}
var id: Int { self.rawValue }
var sexValue: Int {
switch self {
case .men:
return 1
case .women:
return 0
case .mix:
return -1
}
}
var order: Int {
switch self {
case .men:
return 0
case .women:
return 1
case .mix:
return 2
}
}
var buildLabel: String {
switch self {
case .men:
return "H"
case .women:
return "D"
case .mix:
return "M"
}
}
var requestLabel: String {
switch self {
case .men:
return "DM"
case .women:
return "DD"
case .mix:
return "DX"
}
}
var importingRawValue: String {
switch self {
case .men:
return "messieurs"
case .women:
return "dames"
case .mix:
return "mixte"
}
}
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .men:
return displayStyle == .wide ? "Hommes" : "H"
case .women:
return displayStyle == .wide ? "Dames" : "D"
case .mix:
return displayStyle == .wide ? "Mixte" : "MX"
}
}
var rankingDataSourceMale: Bool {
switch self {
case .men:
return true
case .women:
return false
case .mix:
return false
}
}
var playerFilterOption: PlayerFilterOption {
switch self {
case .men:
return .male
case .women:
return .female
case .mix:
return .all
}
}
}
enum GroupStageOrderingMode: Int, Hashable, Codable, CaseIterable, Identifiable {
case random
case snake
case swiss
var id: Int { self.rawValue }
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .random:
return "Au hasard"
case .snake:
return "En serpentin"
case .swiss:
return "Suisse"
}
}
var systemImage: String {
switch self {
case .random:
return "dice.fill"
case .snake:
return "arrow.triangle.swap"
case .swiss:
return "cross.fill"
}
}
}
enum TournamentType: Int, Hashable, Codable, CaseIterable, Identifiable {
case classic
case doubleBrackets
var id: Int { self.rawValue }
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .classic:
return "Classique"
case .doubleBrackets:
return "Double Poules"
}
}
}
enum TeamData: Int, Hashable, Codable, CaseIterable {
case one
case two
var otherTeam: TeamData {
switch self {
case .one:
return .two
case .two:
return .one
}
}
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .one:
return "#1"
case .two:
return "#2"
}
}
}
enum SetFormat: Int, Hashable, Codable {
case nine
case four
case six
case superTieBreak
case megaTieBreak
func shouldTiebreak(scoreTeamOne: Int, scoreTeamTwo: Int) -> Bool {
if let tieBreak {
return (scoreTeamOne + scoreTeamTwo) >= (2 * tieBreak) && (scoreTeamOne == tieBreak || scoreTeamTwo == tieBreak)
} else {
return false
}
}
func winner(teamOne: Int, teamTwo: Int) -> TeamData {
return teamOne >= teamTwo ? .one : .two
}
func hasEnded(teamOne: Int, teamTwo: Int) -> Bool {
switch self {
case .nine:
if teamOne == 9 || teamTwo == 9 {
return true
}
case .four:
if teamOne == 5 || teamTwo == 5 {
return true
}
case .six:
if teamOne == 7 || teamTwo == 7 {
return true
}
case .superTieBreak, .megaTieBreak:
if teamOne == 1 || teamTwo == 1 {
return true
}
}
return (teamOne >= scoreToWin && teamOne >= teamTwo + 2) || (teamTwo >= scoreToWin && teamTwo >= teamOne + 2)
}
var tieBreak: Int? {
switch self {
case .nine:
return 8
case .four:
return 4
case .six:
return 6
case .superTieBreak, .megaTieBreak:
return nil
}
}
func disableValuesForTeamTwo(with teamOneScore: Int) -> [Int] {
switch self {
case .nine:
if teamOneScore == 9 {
return [9]
}
case .four:
if teamOneScore == 4 {
return []
}
case .six:
if teamOneScore == 6 {
return []
}
case .superTieBreak:
if teamOneScore == 10 {
return []
}
case .megaTieBreak:
if teamOneScore == 15 {
return []
}
}
return []
}
var possibleValues: [Int] {
switch self {
case .nine:
return [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
case .four:
return [5, 4, 3, 2, 1, 0]
case .six:
return [7, 6, 5, 4, 3, 2, 1, 0]
case .superTieBreak:
return [10,9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
case .megaTieBreak:
return [15, 14, 13, 12, 11, 10,9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
}
}
var scoreToWin: Int {
switch self {
case .nine:
return 9
case .four:
return 4
case .six:
return 6
case .superTieBreak:
return 10
case .megaTieBreak:
return 15
}
}
var firstGameFormat: Format {
switch self {
case .megaTieBreak:
return .tiebreakFiveTeen
case .superTieBreak:
return .tiebreakTen
default:
return .normal
}
}
}
enum MatchType: String {
case bracket = "bracket"
case groupStage = "groupStage"
case loserBracket = "loserBracket"
}
enum MatchFormat: Int, Hashable, Codable, CaseIterable {
case twoSets
case twoSetsSuperTie
case twoSetsOfFourGames
case nineGames
case superTie
case megaTie
case twoSetsDecisivePoint
case twoSetsDecisivePointSuperTie
case twoSetsOfFourGamesDecisivePoint
case nineGamesDecisivePoint
var weight: Int {
switch self {
case .twoSets, .twoSetsDecisivePoint:
return 0
case .twoSetsSuperTie, .twoSetsDecisivePointSuperTie:
return 1
case .twoSetsOfFourGames, .twoSetsOfFourGamesDecisivePoint:
return 2
case .nineGames, .nineGamesDecisivePoint:
return 3
case .superTie:
return 4
case .megaTie:
return 5
}
}
var rank: Int {
switch self {
case .twoSets:
return 0
case .twoSetsDecisivePoint:
return 1
case .twoSetsSuperTie:
return 2
case .twoSetsDecisivePointSuperTie:
return 3
case .twoSetsOfFourGames:
return 4
case .twoSetsOfFourGamesDecisivePoint:
return 5
case .nineGames:
return 6
case .nineGamesDecisivePoint:
return 7
case .superTie:
return 8
case .megaTie:
return 9
}
}
static func defaultFormatForMatchType(_ matchType: MatchType) -> MatchFormat {
if UserDefaults.standard.object(forKey: matchType.rawValue + "MatchFormatPreference") == nil {
return .nineGamesDecisivePoint
}
return MatchFormat(rawValue: UserDefaults.standard.integer(forKey: matchType.rawValue + "MatchFormatPreference")) ?? .nineGamesDecisivePoint
}
static var allCases: [MatchFormat] {
[.twoSets, .twoSetsDecisivePoint, .twoSetsSuperTie, .twoSetsDecisivePointSuperTie, .twoSetsOfFourGames, .twoSetsOfFourGamesDecisivePoint, .nineGames, .nineGamesDecisivePoint, .superTie, .megaTie]
}
func winner(scoreTeamOne: Int, scoreTeamTwo: Int) -> TeamData {
scoreTeamOne >= scoreTeamTwo ? .one : .two
}
func hasEnded(scoreTeamOne: Int, scoreTeamTwo: Int) -> Bool {
scoreTeamOne == setsToWin || scoreTeamTwo == setsToWin
}
var canSuperTie: Bool {
switch self {
case .twoSetsSuperTie, .twoSetsOfFourGames, .twoSetsOfFourGamesDecisivePoint, .twoSetsDecisivePointSuperTie:
return true
default:
return false
}
}
var estimatedDuration: Int {
if UserDefaults.standard.object(forKey: format) != nil {
return UserDefaults.standard.integer(forKey: format)
} else {
return defaultEstimatedDuration
}
}
var defaultEstimatedDuration: Int {
switch self {
case .twoSets:
return 105
case .twoSetsDecisivePoint:
return 90
case .twoSetsSuperTie:
return 80
case .twoSetsDecisivePointSuperTie:
return 75
case .twoSetsOfFourGames:
return 60
case .twoSetsOfFourGamesDecisivePoint:
return 50
case .nineGames:
return 45
case .nineGamesDecisivePoint:
return 35
case .megaTie:
return 20
case .superTie:
return 15
}
}
var estimatedTimeWithBreak: Int {
estimatedDuration + breakTime.breakTime
}
var breakTime: (breakTime: Int, matchCount: Int) {
switch self {
case .twoSets, .twoSetsDecisivePoint:
return (90, 1)
case .twoSetsSuperTie, .twoSetsDecisivePointSuperTie:
return (60, 1)
case .twoSetsOfFourGames, .twoSetsOfFourGamesDecisivePoint, .nineGames, .nineGamesDecisivePoint:
return (30, 1)
case .superTie:
return (15, 3)
case .megaTie:
return (5, 1)
}
}
func maximumMatchPerDay(for matchCount: Int) -> Int {
switch self {
case .twoSets, .twoSetsDecisivePoint:
return matchCount < 5 ? 2 : 0
case .twoSetsSuperTie, .twoSetsDecisivePointSuperTie:
return matchCount < 6 ? 3 : 0
case .twoSetsOfFourGames, .twoSetsOfFourGamesDecisivePoint, .nineGames, .nineGamesDecisivePoint:
return matchCount < 7 ? 6 : 2
case .superTie:
return 7
case .megaTie:
return 7
}
}
var hasDecisivePoint: Bool {
switch self {
case .nineGamesDecisivePoint, .twoSetsDecisivePoint, .twoSetsOfFourGamesDecisivePoint, .twoSetsDecisivePointSuperTie:
return true
default:
return false
}
}
func newSetFormat(setCount: Int) -> SetFormat {
if setCount == 2 && canSuperTie {
return .superTieBreak
}
return setFormat
}
var suffix: String {
switch self {
case .twoSetsDecisivePoint, .twoSetsDecisivePointSuperTie, .twoSetsOfFourGamesDecisivePoint, .nineGamesDecisivePoint:
return " [Point Décisif]"
default:
return ""
}
}
var longPrefix: String {
return "Format \(format) : "
}
var shortPrefix: String {
return "\(format) : "
}
var format: String {
switch self {
case .twoSets:
return "A1"
case .twoSetsSuperTie:
return "B1"
case .twoSetsOfFourGames:
return "C1"
case .nineGames:
return "D1"
case .superTie:
return "E"
case .megaTie:
return "F"
case .twoSetsDecisivePoint:
return "A2"
case .twoSetsDecisivePointSuperTie:
return "B2"
case .twoSetsOfFourGamesDecisivePoint:
return "C2"
case .nineGamesDecisivePoint:
return "D2"
}
}
var longLabel: String {
switch self {
case .twoSets, .twoSetsDecisivePoint:
return "2 sets de 6"
case .twoSetsSuperTie, .twoSetsDecisivePointSuperTie:
return "2 sets de 6, supertie au 3ème"
case .twoSetsOfFourGames, .twoSetsOfFourGamesDecisivePoint:
return "2 sets de 4, tiebreak à 4/4, supertie au 3ème"
case .nineGames, .nineGamesDecisivePoint:
return "9 jeux, tiebreak à 8/8"
case .superTie:
return "supertie de 10 points"
case .megaTie:
return "supertie de 15 points"
}
}
var computedShortLabelWithoutPrefix: String {
longLabel + suffix
}
var computedShortLabel: String {
shortPrefix + longLabel + suffix
}
var computedLongLabel: String {
longPrefix + longLabel + suffix
}
var setsToWin: Int {
switch self {
case .twoSets, .twoSetsSuperTie, .twoSetsOfFourGames, .twoSetsDecisivePoint, .twoSetsOfFourGamesDecisivePoint, .twoSetsDecisivePointSuperTie:
return 2
case .nineGames, .nineGamesDecisivePoint, .superTie, .megaTie:
return 1
}
}
var setFormat: SetFormat {
switch self {
case .twoSets, .twoSetsSuperTie, .twoSetsDecisivePoint, .twoSetsDecisivePointSuperTie:
return .six
case .twoSetsOfFourGames, .twoSetsOfFourGamesDecisivePoint:
return .four
case .nineGames, .nineGamesDecisivePoint:
return .nine
case .superTie:
return .superTieBreak
case .megaTie:
return .megaTieBreak
}
}
}
enum Format: Int, Hashable, Codable {
case normal
case tiebreakSeven
case tiebreakTen
case tiebreakFiveTeen
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .normal:
return "normal"
case .tiebreakSeven:
return "tie-break en 7"
case .tiebreakTen:
return "tie-break en 10"
case .tiebreakFiveTeen:
return "tie-break en 15"
}
}
var isTiebreak: Bool {
switch self {
case .normal:
return false
case .tiebreakSeven, .tiebreakTen, .tiebreakFiveTeen:
return true
}
}
var scoreToWin: Int? {
switch self {
case .normal:
return nil
case .tiebreakSeven:
return 7
case .tiebreakTen:
return 10
case .tiebreakFiveTeen:
return 15
}
}
}
enum ActionType: Int, Identifiable {
case fault
case winner
var id: Self {
self
}
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .fault:
return "Faute"
case .winner:
return "Point"
}
}
/*
Break points won
Break points
Errors
Smash winners
Smashes
Winners
Volley winners
Total points won
% points won
Total break points
Break points won
Gold points won
Gold points won returning
Gold points won with service
Most consecutive points won
Total set points
Set points won
Match points
*/
}
enum EventType: Int, CaseIterable, Identifiable {
case approvedTournament
case friendlyTournament
case simulation
case animation
var id: Self {
self
}
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .approvedTournament:
return "Tournoi homologué"
case .friendlyTournament:
return "Tournoi amical"
case .simulation:
return "Simulation"
case .animation:
return "Animation"
}
}
}
enum TeamSortingType: Int, Identifiable, CaseIterable, Hashable {
case rank = 1
case inscriptionDate = 2
var id: Int { rawValue }
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .rank:
return "Rang"
case .inscriptionDate:
return "Date d'inscription"
}
}
}
enum PlayersCountRange: Int, CaseIterable {
case N8 = 8
case N12 = 12
case N16 = 16
case N20 = 20
case N24 = 24
case N28 = 28
case N32 = 32
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .N8:
return "4 à 8"
case .N12:
return "9 à 12"
case .N16:
return "13 à 16"
case .N20:
return "17 à 20"
case .N24:
return "21 à 24"
case .N28:
return "25 à 28"
case .N32:
return "29 à 32"
}
}
}
enum RoundLabel {
static let labels = ["Finale", "Demi-finale", "Quart de finale", "8ème de finale", "16ème de finale", "32ème de finale", "64ème de finale"]
static let shortLabels = ["Finale", "Demi", "Quart", "8ème", "16ème", "32ème", "64ème"]
static let colors = ["#d4afb9", "#d1cfe2", "#9cadce", "#7ec4cf", "#daeaf6", "#caffbf"]
static let freeMatchLabels = ["Finale", "Demi-finale", "Quart de finale", "8ème de finale", "16ème de finale", "32ème de finale", "Poule"]
static func loserBrackets(index: Int) -> [String] {
switch index {
case 1:
return ["#3/#4"]
case 2:
return ["#5/#6", "#7/#8"]
default:
return ["#9/#10", "#11/#12", "#13/#14", "#15/#16", "#17/#18", "#19/#20", "#21/#22", "#23/#24", "#25/#26", "#27/#28", "#29/#30", "#31/#32"]
}
}
}
enum AnimationType: Int, CaseIterable, Hashable, Identifiable {
case playerAnimation
case upAndDown
case brawl
var id: Int { rawValue }
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .playerAnimation:
return "Par joueur"
case .upAndDown:
return "Montante / Descandante"
case .brawl:
return "Brawl"
}
}
var descriptionLabel: String {
switch self {
case .playerAnimation:
return "Chaque joueur joue avec quelqu'un de différent à chaque rotation (8 à 12 joueurs)"
case .upAndDown:
return "Les gagnants montent sur le terrain d'à côté, les perdants descendent"
case .brawl:
return "A chaque rotaiton, les gagnants de la rotation pécédente se jouent entre eux"
}
}
}