fix stuff headmanager

main
Razmig Sarkissian 4 weeks ago
parent 13011e2b1c
commit 6c634399d7
  1. 9
      PadelClub/Views/Round/RoundSettingsView.swift
  2. 51
      PadelClub/Views/Tournament/Screen/Components/HeadManagerView.swift
  3. 234
      PadelClub/Views/Tournament/Screen/TableStructureView.swift

@ -160,14 +160,7 @@ struct RoundSettingsView: View {
}
private func _removeRound(_ lastRound: Round) async {
await MainActor.run {
let teams = lastRound.seeds()
teams.forEach { team in
team.resetBracketPosition()
}
tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: teams)
tournamentStore?.rounds.delete(instance: lastRound)
}
await tournament.removeRound(lastRound)
}
}

@ -79,7 +79,7 @@ struct HeadManagerView: View {
valueToAppend = theory
} else {
let lastValue = teamsPerRound.last ?? 0
var newValueToAppend = theory
var newValueToAppend = theory == 0 ? maxAssignable / 2 : theory
if theory > maxAssignable || theory < 0 {
newValueToAppend = valueToAppend / 2
}
@ -102,33 +102,62 @@ struct HeadManagerView: View {
Picker(selection: $selectedSeedRound) {
Text("Choisir").tag(nil as Int?)
ForEach(seedRepartition.indices, id: \.self) { idx in
Text(RoundRule.roundName(fromRoundIndex: idx)).tag(idx)
Text(RoundRule.roundName(fromRoundIndex: idx, displayStyle: .short)).tag(idx)
}
} label: {
Text("Tour contenant la meilleure tête de série")
Text("Tour de la tête de série n°1")
}
.onChange(of: selectedSeedRound) {
seedRepartition = Self.place(heads: heads, teamsInBracket: teamsInBracket, initialSeedRound: selectedSeedRound)
}
} footer: {
FooterButtonView("remise à zéro") {
selectedSeedRound = nil
}
}
Section {
LabeledContent {
Text(teamsInBracket.formatted())
Text(heads.formatted())
} label: {
Text("Effectif du tableau")
Text("Équipes à placer en tableau")
}
if (teamsInBracket - heads) > 0 {
LabeledContent {
Text((teamsInBracket - heads).formatted())
} label: {
Text("Qualifiés entrants")
}
}
LabeledContent {
Text(leftToPlace.formatted())
} label: {
Text("Restant à placer")
}
LabeledContent {
let matchCount = seedRepartition.enumerated().map { (index, value) in
var result = 0
var count = value
if count == 0, let selectedSeedRound, index < selectedSeedRound {
let t = RoundRule.numberOfMatches(forRoundIndex: index)
result = RoundRule.cumulatedNumberOfMatches(forTeams: t * 2)
} else {
if index == seedRepartition.count - 1 {
count += (teamsInBracket - heads)
} else if index == seedRepartition.count - 2 {
count += ((seedRepartition[index + 1] + (teamsInBracket - heads)) / 2)
} else {
count += (seedRepartition[index + 1])
}
result = RoundRule.cumulatedNumberOfMatches(forTeams: count)
}
return result
}
.reduce(0, +)
Text(matchCount.formatted())
} label: {
Text("Matchs estimés")
}
}
Section {
@ -155,13 +184,13 @@ struct HeadManagerView: View {
if headsLeft - maxAssignable > 0 {
valueToAppend = valueToAppend - (headsLeft - maxAssignable)
}
print("Appending to seedRepartition: headsLeft=\(headsLeft), maxAssignable=\(maxAssignable), valueToAppend=\(valueToAppend), current seedRepartition=\(seedRepartition)")
// print("Appending to seedRepartition: headsLeft=\(headsLeft), maxAssignable=\(maxAssignable), valueToAppend=\(valueToAppend), current seedRepartition=\(seedRepartition)")
seedRepartition.append(valueToAppend)
}
}
}
}
.navigationTitle("Têtes de série")
.navigationTitle("Répartition")
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
ButtonValidateView(title: "Valider") {

@ -267,13 +267,13 @@ struct TableStructureView: View {
}
}
if groupStageCount > 0 {
LabeledContent {
Text(tf.formatted())
} label: {
Text("Effectif")
}
}
// if groupStageCount > 0 {
// LabeledContent {
// Text(tf.formatted())
// } label: {
// Text("Effectif tableau")
// }
// }
} else {
LabeledContent {
let mp1 = teamsPerGroupStage * (teamsPerGroupStage - 1) / 2 * groupStageCount
@ -283,16 +283,30 @@ struct TableStructureView: View {
Text("Total de matchs")
}
}
} footer: {
if tsPure > 0 && structurePreset != .doubleGroupStage, groupStageCount > 0 {
if tsPure > teamCount / 2 {
Text("Le nombre de têtes de série ne devrait pas être supérieur à la moitié de l'effectif.").foregroundStyle(.red)
} else if tsPure < teamCount / 8 {
Text("À partir du moment où vous avez des têtes de série, leur nombre ne devrait pas être inférieur à 1/8ème de l'effectif.").foregroundStyle(.red)
} else if tsPure < qualifiedFromGroupStage + groupStageAdditionalQualified {
Text("Le nombre de têtes de série ne devrait pas être inférieur au nombre de paires qualifiées sortantes.").foregroundStyle(.red)
LabeledContent {
FooterButtonView("configurer") {
showSeedRepartition = true
}
} label: {
if tournament.state() == .build {
Text("Répartition des équipes")
} else if selectedTournament != nil {
Text("La configuration du tournoi séléctionné sera utilisée.")
} else {
Text(_seeds())
}
}
.onAppear {
if seedRepartition.isEmpty && tournament.state() == .initial && selectedTournament == nil {
seedRepartition = HeadManagerView.place(heads: tsPure, teamsInBracket: tf, initialSeedRound: nil)
}
}
} footer: {
if tsPure > 0 && structurePreset != .doubleGroupStage, groupStageCount > 0, tsPure < qualifiedFromGroupStage + groupStageAdditionalQualified {
Text("Le nombre de têtes de série ne devrait pas être inférieur au nombre de paires qualifiées sortantes.").foregroundStyle(.red)
}
}
if structurePreset.hasWildcards() && tournament.level.wildcardArePossible() {
@ -303,27 +317,6 @@ struct TableStructureView: View {
}
}
if tournament.state() != .build {
Section {
LabeledContent {
Image(systemName: seedRepartition.isEmpty ? "xmark" : "checkmark")
} label: {
FooterButtonView("Configuration du tableau") {
showSeedRepartition = true
}
.disabled(selectedTournament != nil)
}
} footer: {
if seedRepartition.isEmpty {
Text("Aucune répartition n'a été indiqué, vous devrez réserver ou placer les têtes de séries dans le tableau manuellement.")
} else {
FooterButtonView("Supprimer la configuration", role: .destructive) {
seedRepartition = []
}
}
}
}
if tournament.rounds().isEmpty && tournament.state() == .build {
Section {
RowButtonView("Ajouter un tableau", role: .destructive) {
@ -336,6 +329,12 @@ struct TableStructureView: View {
if tournament.state() != .initial {
if seedRepartition.isEmpty == false {
RowButtonView("Modifier la répartition des équipes en tableau", role: .destructive, confirmationMessage: "Cette action va effacer le répartition actuelle des équipes dans le tableau.") {
await _handleSeedRepartition()
}
}
Section {
RowButtonView("Sauver sans reconstuire l'existant") {
_saveWithoutRebuild()
@ -370,13 +369,6 @@ struct TableStructureView: View {
}
}
.toolbarBackground(.visible, for: .navigationBar)
.onChange(of: teamCount) {
if teamCount != tournament.teamCount {
updatedElements.insert(.teamCount)
} else {
updatedElements.remove(.teamCount)
}
}
.sheet(isPresented: $showSeedRepartition, content: {
NavigationStack {
HeadManagerView(teamsInBracket: tf, heads: tsPure, initialSeedRepartition: seedRepartition) { seedRepartition in
@ -384,6 +376,14 @@ struct TableStructureView: View {
}
}
})
.onChange(of: teamCount) {
if teamCount != tournament.teamCount {
updatedElements.insert(.teamCount)
} else {
updatedElements.remove(.teamCount)
}
_verifyValueIntegrity()
}
.onChange(of: groupStageCount) {
if groupStageCount != tournament.groupStageCount {
updatedElements.insert(.groupStageCount)
@ -394,25 +394,31 @@ struct TableStructureView: View {
if structurePreset.isFederalPreset(), groupStageCount == 0 {
teamCount = structurePreset.tableDimension()
}
_verifyValueIntegrity()
}
.onChange(of: teamsPerGroupStage) {
if teamsPerGroupStage != tournament.teamsPerGroupStage {
updatedElements.insert(.teamsPerGroupStage)
} else {
updatedElements.remove(.teamsPerGroupStage)
} }
}
_verifyValueIntegrity()
}
.onChange(of: qualifiedPerGroupStage) {
if qualifiedPerGroupStage != tournament.qualifiedPerGroupStage {
updatedElements.insert(.qualifiedPerGroupStage)
} else {
updatedElements.remove(.qualifiedPerGroupStage)
} }
}
_verifyValueIntegrity()
}
.onChange(of: groupStageAdditionalQualified) {
if groupStageAdditionalQualified != tournament.groupStageAdditionalQualified {
updatedElements.insert(.groupStageAdditionalQualified)
} else {
updatedElements.remove(.groupStageAdditionalQualified)
}
_verifyValueIntegrity()
}
.toolbar {
if tournament.state() != .initial {
@ -485,6 +491,19 @@ struct TableStructureView: View {
}
private func _seeds() -> String {
if seedRepartition.isEmpty || seedRepartition.reduce(0, +) == 0 {
return "Aucune configuration"
}
return seedRepartition.enumerated().compactMap { (index, count) in
if count > 0 {
return RoundRule.roundName(fromRoundIndex: index) + " : \(count)"
} else {
return nil
}
}.joined(separator: ", ")
}
private func _reset() {
tournament.removeWildCards()
tournament.deleteGroupStages()
@ -573,12 +592,6 @@ struct TableStructureView: View {
}
tournament.deleteAndBuildEverything(preset: structurePreset)
if seedRepartition.count > 0 {
while tournament.rounds().count < seedRepartition.count {
await tournament.addNewRound(tournament.rounds().count)
}
}
if let selectedTournament {
let oldTournamentStart = selectedTournament.startDate
let newTournamentStart = tournament.startDate
@ -614,66 +627,10 @@ struct TableStructureView: View {
}
tournament.tournamentStore?.matches.addOrUpdate(contentOfs: tournament._allMatchesIncludingDisabled())
} else {
for (index, seedCount) in seedRepartition.enumerated() {
if let round = tournament.rounds().first(where: { $0.index == index }) {
let baseIndex = RoundRule.baseIndex(forRoundIndex: round.index)
let numberOfMatches = RoundRule.numberOfMatches(forRoundIndex: round.index)
let playedMatches = round.playedMatches().map { $0.index - baseIndex }
let allMatches = round._matches()
let seedSorted = frenchUmpireOrder(for: numberOfMatches).filter({ index in
playedMatches.contains(index)
}).prefix(seedCount)
for (index, value) in seedSorted.enumerated() {
let isOpponentTurn = index >= playedMatches.count
if let match = allMatches[safe:value] {
if match.index - baseIndex < numberOfMatches / 2 {
if isOpponentTurn {
match.previousMatch(.two)?.disableMatch()
} else {
match.previousMatch(.one)?.disableMatch()
}
} else {
if isOpponentTurn {
match.previousMatch(.one)?.disableMatch()
} else {
match.previousMatch(.two)?.disableMatch()
}
}
}
}
if seedCount > 0 {
tournament.tournamentStore?.matches.addOrUpdate(contentOfs: round._matches())
tournament.tournamentStore?.matches.addOrUpdate(contentOfs: round.allLoserRoundMatches())
round.deleteLoserBracket()
round.buildLoserBracket()
round.loserRounds().forEach { loserRound in
loserRound.disableUnplayedLoserBracketMatches()
}
}
}
}
}
// if initialSeedRound > 0 {
// if let round = tournament.rounds().first(where: { $0.index == initialSeedRound }) {
// let seedSorted = frenchUmpireOrder(for: RoundRule.numberOfMatches(forRoundIndex: round.index))
// print(seedSorted)
// seedSorted.prefix(initialSeedCount).forEach { index in
// if let match = round._matches()[safe:index] {
// if match.indexInRound() < RoundRule.numberOfMatches(forRoundIndex: round.index) / 2 {
// match.previousMatch(.one)?.disableMatch()
// } else {
// match.previousMatch(.two)?.disableMatch()
// }
// }
// }
//
// if initialSeedCount > 0 {
// tournament.tournamentStore?.matches.addOrUpdate(contentOfs: tournament._allMatchesIncludingDisabled())
// }
// }
// }
if seedRepartition.count > 0 {
await _handleSeedRepartition()
}
} else if (rebuildEverything == false && requirements.contains(.groupStage)) {
tournament.deleteGroupStages()
@ -693,8 +650,63 @@ struct TableStructureView: View {
}
}
private func _handleSeedRepartition() async {
while tournament.rounds().count < seedRepartition.count {
await tournament.addNewRound(tournament.rounds().count)
}
if seedRepartition.reduce(0, +) > 0 {
let rounds = tournament.rounds()
let roundsToDelete = rounds.suffix(rounds.count - seedRepartition.count)
for round in roundsToDelete {
await tournament.removeRound(round)
}
}
for (index, seedCount) in seedRepartition.enumerated() {
if let round = tournament.rounds().first(where: { $0.index == index }) {
let baseIndex = RoundRule.baseIndex(forRoundIndex: round.index)
let numberOfMatches = RoundRule.numberOfMatches(forRoundIndex: round.index)
let playedMatches = round.playedMatches().map { $0.index - baseIndex }
let allMatches = round._matches()
let seedSorted = frenchUmpireOrder(for: numberOfMatches).filter({ index in
playedMatches.contains(index)
}).prefix(seedCount)
for (index, value) in seedSorted.enumerated() {
let isOpponentTurn = index >= playedMatches.count
if let match = allMatches[safe:value] {
if match.index - baseIndex < numberOfMatches / 2 {
if isOpponentTurn {
match.previousMatch(.two)?.disableMatch()
} else {
match.previousMatch(.one)?.disableMatch()
}
} else {
if isOpponentTurn {
match.previousMatch(.one)?.disableMatch()
} else {
match.previousMatch(.two)?.disableMatch()
}
}
}
}
if seedCount > 0 {
tournament.tournamentStore?.matches.addOrUpdate(contentOfs: round._matches())
tournament.tournamentStore?.matches.addOrUpdate(contentOfs: round.allLoserRoundMatches())
round.deleteLoserBracket()
round.buildLoserBracket()
round.loserRounds().forEach { loserRound in
loserRound.disableUnplayedLoserBracketMatches()
}
}
}
}
}
private func _updatePreset() {
if let selectedTournament {
seedRepartition = []
teamCount = selectedTournament.teamCount
groupStageCount = selectedTournament.groupStageCount
teamsPerGroupStage = selectedTournament.teamsPerGroupStage
@ -709,6 +721,7 @@ struct TableStructureView: View {
groupStageAdditionalQualified = 0
buildWildcards = tournament.level.wildcardArePossible()
}
_verifyValueIntegrity()
}
private func _verifyValueIntegrity() {
@ -754,6 +767,7 @@ struct TableStructureView: View {
}
}
seedRepartition = HeadManagerView.place(heads: tsPure, teamsInBracket: tf, initialSeedRound: nil)
}
}

Loading…
Cancel
Save