|
|
|
|
@ -46,10 +46,7 @@ class Tournament : ModelObject, Storable { |
|
|
|
|
|
|
|
|
|
@ObservationIgnored |
|
|
|
|
var navigationPath: [Screen] = [] |
|
|
|
|
|
|
|
|
|
@ObservationIgnored |
|
|
|
|
var undoManager: Int = 0 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
internal init(event: String? = nil, creator: String? = nil, name: String? = nil, startDate: Date = Date(), endDate: Date? = nil, creationDate: Date = Date(), isPrivate: Bool = true, groupStageFormat: Int? = nil, roundFormat: Int? = nil, loserRoundFormat: Int? = nil, groupStageSortMode: GroupStageOrderingMode, groupStageCount: Int = 4, rankSourceDate: Date? = nil, dayDuration: Int = 1, teamCount: Int = 24, teamSorting: TeamSortingType? = nil, federalCategory: TournamentCategory, federalLevelCategory: TournamentLevel, federalAgeCategory: FederalTournamentAge, groupStageCourtCount: Int? = nil, seedCount: Int = 8, closedRegistrationDate: Date? = nil, groupStageAdditionalQualified: Int = 0, courtCount: Int = 2, prioritizeClubMembers: Bool = false, qualifiedPerGroupStage: Int = 1, teamsPerGroupStage: Int = 4, entryFee: Double? = nil, maleUnrankedValue: Int? = nil, femaleUnrankedValue: Int? = nil) { |
|
|
|
|
self.event = event |
|
|
|
|
self.creator = creator |
|
|
|
|
@ -131,6 +128,115 @@ class Tournament : ModelObject, Storable { |
|
|
|
|
return .initial |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func seeds() -> [TeamRegistration] { |
|
|
|
|
let seeds = max(teamCount - groupStageCount * teamsPerGroupStage, 0) |
|
|
|
|
return Array(selectedSortedTeams().prefix(seeds)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func availableSeeds() -> [TeamRegistration] { |
|
|
|
|
return seeds().filter { $0.isSeedable() } |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func lastSeedRound() -> Int? { |
|
|
|
|
if let last = seeds().filter({ $0.bracketPosition != nil }).last { |
|
|
|
|
return RoundRule.roundIndex(fromMatchIndex: last.bracketPosition! / 2) |
|
|
|
|
} else { |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func getRound(atRoundIndex roundIndex: Int) -> Round? { |
|
|
|
|
Store.main.filter(isIncluded: { $0.tournament == id && $0.index == roundIndex }).first |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func availableSeedSpot(inRoundIndex roundIndex: Int) -> [Match] { |
|
|
|
|
getRound(atRoundIndex: roundIndex)?.matches.filter { $0.teams().count == 0 } ?? [] |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func availableSeedOpponentSpot(inRoundIndex roundIndex: Int) -> [Match] { |
|
|
|
|
getRound(atRoundIndex: roundIndex)?.matches.filter { $0.teams().count == 1 } ?? [] |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func availableSeedGroups() -> [SeedInterval] { |
|
|
|
|
let seeds = seeds() |
|
|
|
|
var availableSeedGroup = Set<SeedInterval>() |
|
|
|
|
for (index, seed) in seeds.enumerated() { |
|
|
|
|
if seed.isSeedable(), let seedGroup = seedGroup(for: index) { |
|
|
|
|
availableSeedGroup.insert(seedGroup) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return availableSeedGroup.sorted(by: <) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func seedGroup(for alreadySetupSeeds: Int) -> SeedInterval? { |
|
|
|
|
switch alreadySetupSeeds { |
|
|
|
|
case 0...1: |
|
|
|
|
return SeedInterval(first: 1, last: 2) |
|
|
|
|
case 2...3: |
|
|
|
|
return SeedInterval(first: 3, last: 4) |
|
|
|
|
case 4...7: |
|
|
|
|
return SeedInterval(first: 5, last: 8) |
|
|
|
|
case 8...15: |
|
|
|
|
// if 16 - 9 > availableSeeds().count { |
|
|
|
|
// switch alreadySetupSeeds { |
|
|
|
|
// case 8...15: |
|
|
|
|
// return SeedInterval(first: 5, last: 8) |
|
|
|
|
// case 8...15: |
|
|
|
|
// return SeedInterval(first: 5, last: 8) |
|
|
|
|
// } |
|
|
|
|
return SeedInterval(first: 9, last: 16) |
|
|
|
|
case 16...23: |
|
|
|
|
return SeedInterval(first: 17, last: 24) |
|
|
|
|
case 24...31: |
|
|
|
|
return SeedInterval(first: 25, last: 32) |
|
|
|
|
default: |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func availableSeedGroup() -> SeedInterval? { |
|
|
|
|
let seeds = seeds() |
|
|
|
|
if let firstIndex = seeds.firstIndex(where: { $0.isSeedable() }) { |
|
|
|
|
guard let seedGroup = seedGroup(for: firstIndex) else { return nil } |
|
|
|
|
return seedGroup |
|
|
|
|
} |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func randomSeed(fromSeedGroup seedGroup: SeedInterval) -> TeamRegistration? { |
|
|
|
|
let availableSeeds = seeds(inSeedGroup: seedGroup) |
|
|
|
|
return availableSeeds.randomElement() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func seeds(inSeedGroup seedGroup: SeedInterval) -> [TeamRegistration] { |
|
|
|
|
let availableSeedInSeedGroup = (seedGroup.last - seedGroup.first) + 1 |
|
|
|
|
let availableSeeds = seeds().dropFirst(seedGroup.first - 1).prefix(availableSeedInSeedGroup).filter({ $0.isSeedable() }) |
|
|
|
|
return availableSeeds |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func setSeeds(inRoundIndex roundIndex: Int, inSeedGroup seedGroup: SeedInterval) { |
|
|
|
|
let availableSeedSpot = availableSeedSpot(inRoundIndex: roundIndex) |
|
|
|
|
let availableSeedOpponentSpot = availableSeedOpponentSpot(inRoundIndex: roundIndex) |
|
|
|
|
let availableSeeds = seeds(inSeedGroup: seedGroup) |
|
|
|
|
|
|
|
|
|
if availableSeeds.count <= availableSeedSpot.count { |
|
|
|
|
let spots = availableSeedSpot.shuffled() |
|
|
|
|
for (index, seed) in availableSeeds.enumerated() { |
|
|
|
|
seed.setSeedPosition(inSpot: spots[index], upperBranch: nil, opposingSeeding: false) |
|
|
|
|
} |
|
|
|
|
} else if (availableSeeds.count <= availableSeedOpponentSpot.count && availableSeeds.count == self.availableSeeds().count) { |
|
|
|
|
|
|
|
|
|
let spots = availableSeedOpponentSpot.shuffled() |
|
|
|
|
for (index, seed) in availableSeeds.enumerated() { |
|
|
|
|
seed.setSeedPosition(inSpot: spots[index], upperBranch: nil, opposingSeeding: true) |
|
|
|
|
} |
|
|
|
|
} else if let chunk = seedGroup.chunk() { |
|
|
|
|
setSeeds(inRoundIndex: roundIndex, inSeedGroup: chunk) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func inscriptionClosed() -> Bool { |
|
|
|
|
closedRegistrationDate != nil |
|
|
|
|
} |
|
|
|
|
@ -260,11 +366,6 @@ class Tournament : ModelObject, Storable { |
|
|
|
|
var clubName: String? { |
|
|
|
|
nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
//todo |
|
|
|
|
func roundCount() -> Int { |
|
|
|
|
4 |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
//todo |
|
|
|
|
func significantPlayerCount() -> Int { |
|
|
|
|
@ -590,7 +691,7 @@ class Tournament : ModelObject, Storable { |
|
|
|
|
|
|
|
|
|
var matchFormat: MatchFormat { |
|
|
|
|
get { |
|
|
|
|
MatchFormat(rawValue: roundFormat ?? 0) ?? .defaultFormatForMatchType(.bracket) |
|
|
|
|
MatchFormat(rawValue: roundFormat) ?? .defaultFormatForMatchType(.bracket) |
|
|
|
|
} |
|
|
|
|
set { |
|
|
|
|
roundFormat = newValue.rawValue |
|
|
|
|
@ -599,7 +700,7 @@ class Tournament : ModelObject, Storable { |
|
|
|
|
|
|
|
|
|
var groupStageMatchFormat: MatchFormat { |
|
|
|
|
get { |
|
|
|
|
MatchFormat(rawValue: groupStageFormat ?? 0) ?? .defaultFormatForMatchType(.groupStage) |
|
|
|
|
MatchFormat(rawValue: groupStageFormat) ?? .defaultFormatForMatchType(.groupStage) |
|
|
|
|
} |
|
|
|
|
set { |
|
|
|
|
groupStageFormat = newValue.rawValue |
|
|
|
|
@ -608,7 +709,7 @@ class Tournament : ModelObject, Storable { |
|
|
|
|
|
|
|
|
|
var loserBracketMatchFormat: MatchFormat { |
|
|
|
|
get { |
|
|
|
|
MatchFormat(rawValue: loserRoundFormat ?? 0) ?? .defaultFormatForMatchType(.loserBracket) |
|
|
|
|
MatchFormat(rawValue: loserRoundFormat) ?? .defaultFormatForMatchType(.loserBracket) |
|
|
|
|
} |
|
|
|
|
set { |
|
|
|
|
loserRoundFormat = newValue.rawValue |
|
|
|
|
@ -662,8 +763,7 @@ class Tournament : ModelObject, Storable { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func loserBracketSmartMatchFormat(_ roundIndex: Int) -> MatchFormat { |
|
|
|
|
let idx = roundCount() - roundIndex |
|
|
|
|
let format = tournamentLevel.federalFormatForLoserBracketRound(idx) |
|
|
|
|
let format = tournamentLevel.federalFormatForLoserBracketRound(roundIndex) |
|
|
|
|
if loserBracketMatchFormat.rank > format.rank { |
|
|
|
|
return format |
|
|
|
|
} else { |
|
|
|
|
@ -681,8 +781,7 @@ class Tournament : ModelObject, Storable { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func roundSmartMatchFormat(_ roundIndex: Int) -> MatchFormat { |
|
|
|
|
let idx = roundCount() - roundIndex |
|
|
|
|
let format = tournamentLevel.federalFormatForBracketRound(idx) |
|
|
|
|
let format = tournamentLevel.federalFormatForBracketRound(roundIndex) |
|
|
|
|
if matchFormat.rank > format.rank { |
|
|
|
|
return format |
|
|
|
|
} else { |
|
|
|
|
|