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. 236
      PadelClub/Views/Tournament/Screen/TableStructureView.swift

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

@ -79,7 +79,7 @@ struct HeadManagerView: View {
valueToAppend = theory valueToAppend = theory
} else { } else {
let lastValue = teamsPerRound.last ?? 0 let lastValue = teamsPerRound.last ?? 0
var newValueToAppend = theory var newValueToAppend = theory == 0 ? maxAssignable / 2 : theory
if theory > maxAssignable || theory < 0 { if theory > maxAssignable || theory < 0 {
newValueToAppend = valueToAppend / 2 newValueToAppend = valueToAppend / 2
} }
@ -102,33 +102,62 @@ struct HeadManagerView: View {
Picker(selection: $selectedSeedRound) { Picker(selection: $selectedSeedRound) {
Text("Choisir").tag(nil as Int?) Text("Choisir").tag(nil as Int?)
ForEach(seedRepartition.indices, id: \.self) { idx in ForEach(seedRepartition.indices, id: \.self) { idx in
Text(RoundRule.roundName(fromRoundIndex: idx)).tag(idx) Text(RoundRule.roundName(fromRoundIndex: idx, displayStyle: .short)).tag(idx)
} }
} label: { } 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) { .onChange(of: selectedSeedRound) {
seedRepartition = Self.place(heads: heads, teamsInBracket: teamsInBracket, initialSeedRound: selectedSeedRound) seedRepartition = Self.place(heads: heads, teamsInBracket: teamsInBracket, initialSeedRound: selectedSeedRound)
} }
} footer: {
FooterButtonView("remise à zéro") {
selectedSeedRound = nil
}
} }
Section { Section {
LabeledContent { LabeledContent {
Text(teamsInBracket.formatted()) Text(heads.formatted())
} label: { } 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 { LabeledContent {
Text(leftToPlace.formatted()) Text(leftToPlace.formatted())
} label: { } label: {
Text("Restant à placer") 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 { Section {
@ -155,13 +184,13 @@ struct HeadManagerView: View {
if headsLeft - maxAssignable > 0 { if headsLeft - maxAssignable > 0 {
valueToAppend = valueToAppend - (headsLeft - maxAssignable) 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) seedRepartition.append(valueToAppend)
} }
} }
} }
} }
.navigationTitle("Têtes de série") .navigationTitle("Répartition")
.toolbar { .toolbar {
ToolbarItem(placement: .topBarTrailing) { ToolbarItem(placement: .topBarTrailing) {
ButtonValidateView(title: "Valider") { ButtonValidateView(title: "Valider") {

@ -267,13 +267,13 @@ struct TableStructureView: View {
} }
} }
if groupStageCount > 0 { // if groupStageCount > 0 {
LabeledContent { // LabeledContent {
Text(tf.formatted()) // Text(tf.formatted())
} label: { // } label: {
Text("Effectif") // Text("Effectif tableau")
} // }
} // }
} else { } else {
LabeledContent { LabeledContent {
let mp1 = teamsPerGroupStage * (teamsPerGroupStage - 1) / 2 * groupStageCount let mp1 = teamsPerGroupStage * (teamsPerGroupStage - 1) / 2 * groupStageCount
@ -283,16 +283,30 @@ struct TableStructureView: View {
Text("Total de matchs") Text("Total de matchs")
} }
} }
} footer: {
if tsPure > 0 && structurePreset != .doubleGroupStage, groupStageCount > 0 { LabeledContent {
if tsPure > teamCount / 2 { FooterButtonView("configurer") {
Text("Le nombre de têtes de série ne devrait pas être supérieur à la moitié de l'effectif.").foregroundStyle(.red) showSeedRepartition = true
} 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) } label: {
} else if tsPure < qualifiedFromGroupStage + groupStageAdditionalQualified { if tournament.state() == .build {
Text("Le nombre de têtes de série ne devrait pas être inférieur au nombre de paires qualifiées sortantes.").foregroundStyle(.red) 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() { 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 { if tournament.rounds().isEmpty && tournament.state() == .build {
Section { Section {
RowButtonView("Ajouter un tableau", role: .destructive) { RowButtonView("Ajouter un tableau", role: .destructive) {
@ -336,6 +329,12 @@ struct TableStructureView: View {
if tournament.state() != .initial { 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 { Section {
RowButtonView("Sauver sans reconstuire l'existant") { RowButtonView("Sauver sans reconstuire l'existant") {
_saveWithoutRebuild() _saveWithoutRebuild()
@ -370,13 +369,6 @@ struct TableStructureView: View {
} }
} }
.toolbarBackground(.visible, for: .navigationBar) .toolbarBackground(.visible, for: .navigationBar)
.onChange(of: teamCount) {
if teamCount != tournament.teamCount {
updatedElements.insert(.teamCount)
} else {
updatedElements.remove(.teamCount)
}
}
.sheet(isPresented: $showSeedRepartition, content: { .sheet(isPresented: $showSeedRepartition, content: {
NavigationStack { NavigationStack {
HeadManagerView(teamsInBracket: tf, heads: tsPure, initialSeedRepartition: seedRepartition) { seedRepartition in 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) { .onChange(of: groupStageCount) {
if groupStageCount != tournament.groupStageCount { if groupStageCount != tournament.groupStageCount {
updatedElements.insert(.groupStageCount) updatedElements.insert(.groupStageCount)
@ -394,25 +394,31 @@ struct TableStructureView: View {
if structurePreset.isFederalPreset(), groupStageCount == 0 { if structurePreset.isFederalPreset(), groupStageCount == 0 {
teamCount = structurePreset.tableDimension() teamCount = structurePreset.tableDimension()
} }
_verifyValueIntegrity()
} }
.onChange(of: teamsPerGroupStage) { .onChange(of: teamsPerGroupStage) {
if teamsPerGroupStage != tournament.teamsPerGroupStage { if teamsPerGroupStage != tournament.teamsPerGroupStage {
updatedElements.insert(.teamsPerGroupStage) updatedElements.insert(.teamsPerGroupStage)
} else { } else {
updatedElements.remove(.teamsPerGroupStage) updatedElements.remove(.teamsPerGroupStage)
} } }
_verifyValueIntegrity()
}
.onChange(of: qualifiedPerGroupStage) { .onChange(of: qualifiedPerGroupStage) {
if qualifiedPerGroupStage != tournament.qualifiedPerGroupStage { if qualifiedPerGroupStage != tournament.qualifiedPerGroupStage {
updatedElements.insert(.qualifiedPerGroupStage) updatedElements.insert(.qualifiedPerGroupStage)
} else { } else {
updatedElements.remove(.qualifiedPerGroupStage) updatedElements.remove(.qualifiedPerGroupStage)
} } }
_verifyValueIntegrity()
}
.onChange(of: groupStageAdditionalQualified) { .onChange(of: groupStageAdditionalQualified) {
if groupStageAdditionalQualified != tournament.groupStageAdditionalQualified { if groupStageAdditionalQualified != tournament.groupStageAdditionalQualified {
updatedElements.insert(.groupStageAdditionalQualified) updatedElements.insert(.groupStageAdditionalQualified)
} else { } else {
updatedElements.remove(.groupStageAdditionalQualified) updatedElements.remove(.groupStageAdditionalQualified)
} }
_verifyValueIntegrity()
} }
.toolbar { .toolbar {
if tournament.state() != .initial { if tournament.state() != .initial {
@ -484,6 +490,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() { private func _reset() {
tournament.removeWildCards() tournament.removeWildCards()
@ -573,12 +592,6 @@ struct TableStructureView: View {
} }
tournament.deleteAndBuildEverything(preset: structurePreset) tournament.deleteAndBuildEverything(preset: structurePreset)
if seedRepartition.count > 0 {
while tournament.rounds().count < seedRepartition.count {
await tournament.addNewRound(tournament.rounds().count)
}
}
if let selectedTournament { if let selectedTournament {
let oldTournamentStart = selectedTournament.startDate let oldTournamentStart = selectedTournament.startDate
let newTournamentStart = tournament.startDate let newTournamentStart = tournament.startDate
@ -614,66 +627,10 @@ struct TableStructureView: View {
} }
tournament.tournamentStore?.matches.addOrUpdate(contentOfs: tournament._allMatchesIncludingDisabled()) tournament.tournamentStore?.matches.addOrUpdate(contentOfs: tournament._allMatchesIncludingDisabled())
} else { }
for (index, seedCount) in seedRepartition.enumerated() {
if let round = tournament.rounds().first(where: { $0.index == index }) { if seedRepartition.count > 0 {
let baseIndex = RoundRule.baseIndex(forRoundIndex: round.index) await _handleSeedRepartition()
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())
// }
// }
// }
} }
} else if (rebuildEverything == false && requirements.contains(.groupStage)) { } else if (rebuildEverything == false && requirements.contains(.groupStage)) {
tournament.deleteGroupStages() 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() { private func _updatePreset() {
if let selectedTournament { if let selectedTournament {
seedRepartition = []
teamCount = selectedTournament.teamCount teamCount = selectedTournament.teamCount
groupStageCount = selectedTournament.groupStageCount groupStageCount = selectedTournament.groupStageCount
teamsPerGroupStage = selectedTournament.teamsPerGroupStage teamsPerGroupStage = selectedTournament.teamsPerGroupStage
@ -709,6 +721,7 @@ struct TableStructureView: View {
groupStageAdditionalQualified = 0 groupStageAdditionalQualified = 0
buildWildcards = tournament.level.wildcardArePossible() buildWildcards = tournament.level.wildcardArePossible()
} }
_verifyValueIntegrity()
} }
private func _verifyValueIntegrity() { private func _verifyValueIntegrity() {
@ -754,6 +767,7 @@ struct TableStructureView: View {
} }
} }
seedRepartition = HeadManagerView.place(heads: tsPure, teamsInBracket: tf, initialSeedRound: nil)
} }
} }

Loading…
Cancel
Save