still fixing stuff on loserbracket management

multistore
Razmig Sarkissian 2 years ago
parent c0475025ab
commit e5086fac19
  1. 6
      PadelClub/Data/Match.swift
  2. 49
      PadelClub/Data/Round.swift
  3. 1
      PadelClub/Data/TeamRegistration.swift
  4. 75
      PadelClub/Data/Tournament.swift
  5. 9
      PadelClub/ViewModel/SeedInterval.swift
  6. 1
      PadelClub/Views/Match/PlayerBlockView.swift
  7. 14
      PadelClub/Views/Round/LoserRoundsView.swift
  8. 87
      PadelClub/Views/Round/RoundSettingsView.swift
  9. 11
      PadelClub/Views/Round/RoundView.swift

@ -126,19 +126,17 @@ class Match: ModelObject, Storable {
_toggleMatchDisableState(false) _toggleMatchDisableState(false)
} }
func _toggleLoserMatchDisableState(_ state: Bool) { private func _toggleLoserMatchDisableState(_ state: Bool) {
if isLoserBracket == false { if isLoserBracket == false {
let indexInRound = RoundRule.matchIndexWithinRound(fromMatchIndex: index) let indexInRound = RoundRule.matchIndexWithinRound(fromMatchIndex: index)
if let loserMatch = roundObject?.loserRounds().first?.getMatch(atMatchIndexInRound: indexInRound / 2) { if let loserMatch = roundObject?.loserRounds().first?.getMatch(atMatchIndexInRound: indexInRound / 2) {
print("disabling first loserround", state, loserMatch.matchTitle(.wide))
loserMatch.disabled = state loserMatch.disabled = state
try? DataStore.shared.matches.addOrUpdate(instance: loserMatch) try? DataStore.shared.matches.addOrUpdate(instance: loserMatch)
loserMatch._toggleLoserMatchDisableState(state) loserMatch._toggleLoserMatchDisableState(state)
} }
} else { } else {
roundObject?.loserRounds().forEach({ round in roundObject?.loserRounds().forEach({ round in
print("disabling", state, round.roundTitle()) round.handleLoserRoundState()
round.disableLoserRound(state)
}) })
} }
} }

@ -140,10 +140,10 @@ class Round: ModelObject, Storable {
func getMatch(atMatchIndexInRound matchIndexInRound: Int) -> Match? { func getMatch(atMatchIndexInRound matchIndexInRound: Int) -> Match? {
_matches().first(where: { Store.main.filter(isIncluded: {
let index = RoundRule.matchIndexWithinRound(fromMatchIndex: $0.index) let index = RoundRule.matchIndexWithinRound(fromMatchIndex: $0.index)
return index == matchIndexInRound return $0.round == id && index == matchIndexInRound
}) }).first
} }
func playedMatches() -> [Match] { func playedMatches() -> [Match] {
@ -176,32 +176,53 @@ class Round: ModelObject, Storable {
} }
func enableRound() { func enableRound() {
_toggleRound(disable: false)
}
func disableRound() {
_toggleRound(disable: true)
}
private func _toggleRound(disable: Bool) {
let _matches = _matches() let _matches = _matches()
_matches.forEach { match in _matches.forEach { match in
match.disabled = false match.disabled = disable
match.resetMatch() match.resetMatch()
try? DataStore.shared.teamScores.delete(contentOfs: match.teamScores) try? DataStore.shared.teamScores.delete(contentOfs: match.teamScores)
} }
try? DataStore.shared.matches.addOrUpdate(contentOfs: _matches) try? DataStore.shared.matches.addOrUpdate(contentOfs: _matches)
} }
func disableLoserRound(_ disable: Bool) { func handleLoserRoundState() {
let _matches = _matches() let _matches = _matches()
_matches.forEach { match in _matches.forEach { match in
if disable { let previousRound = self.previousRound()
if upperBracketTopMatch(ofMatchIndex: match.index)?.disabled == true || upperBracketBottomMatch(ofMatchIndex: match.index)?.disabled == true { let indexInRound = RoundRule.matchIndexWithinRound(fromMatchIndex: match.index)
match.disabled = true var parentMatches = [Match]()
} if isLoserBracket(), previousRound == nil, let parentRound = parentRound {
} else { let upperBracketTopMatch = parentRound.getMatch(atMatchIndexInRound: indexInRound * 2)
if upperBracketTopMatch(ofMatchIndex: match.index)?.disabled == false && upperBracketBottomMatch(ofMatchIndex: match.index)?.disabled == false { let upperBracketBottomMatch = parentRound.getMatch(atMatchIndexInRound: indexInRound * 2 + 1)
match.disabled = false parentMatches = [upperBracketTopMatch, upperBracketBottomMatch].compactMap({ $0 })
} } else if let previousRound {
let previousRoundTopMatch : Match? = Store.main.filter {
$0.round == previousRound.id && $0.index == match.topPreviousRoundMatchIndex()
}.first
let previousRoundBottomMatch : Match? = Store.main.filter {
$0.round == previousRound.id && $0.index == match.bottomPreviousRoundMatchIndex()
}.first
parentMatches = [previousRoundTopMatch, previousRoundBottomMatch].compactMap({ $0 })
}
if parentMatches.anySatisfy({ $0.disabled }) {
match.disabled = true
} else if parentMatches.allSatisfy({ $0.disabled == false }) {
match.disabled = false
} }
} }
try? DataStore.shared.matches.addOrUpdate(contentOfs: _matches) try? DataStore.shared.matches.addOrUpdate(contentOfs: _matches)
loserRounds().forEach { round in loserRounds().forEach { round in
round.disableLoserRound(disable) round.handleLoserRoundState()
} }
} }

@ -62,7 +62,6 @@ class TeamRegistration: ModelObject, Storable {
if opposingSeeding { if opposingSeeding {
teamPosition = slot ?? (isUpper ? .two : .one) teamPosition = slot ?? (isUpper ? .two : .one)
} }
match.enableMatch()
match.previousMatch(teamPosition)?.disableMatch() match.previousMatch(teamPosition)?.disableMatch()
bracketPosition = matchIndex * 2 + teamPosition.rawValue bracketPosition = matchIndex * 2 + teamPosition.rawValue
} }

@ -143,11 +143,11 @@ class Tournament : ModelObject, Storable {
return seeds().filter { $0.isSeedable() } return seeds().filter { $0.isSeedable() }
} }
func lastSeedRound() -> Int? { func lastSeedRound() -> Int {
if let last = seeds().filter({ $0.bracketPosition != nil }).last { if let last = seeds().filter({ $0.bracketPosition != nil }).last {
return RoundRule.roundIndex(fromMatchIndex: last.bracketPosition! / 2) return RoundRule.roundIndex(fromMatchIndex: last.bracketPosition! / 2)
} else { } else {
return nil return 0
} }
} }
@ -213,24 +213,67 @@ class Tournament : ModelObject, Storable {
return availableSeeds return availableSeeds
} }
func setSeeds(inRoundIndex roundIndex: Int, inSeedGroup seedGroup: SeedInterval) { func seedGroupAvailable(atRoundIndex roundIndex: Int) -> SeedInterval? {
let availableSeedSpot = availableSeedSpot(inRoundIndex: roundIndex) if let availableSeedGroup = availableSeedGroup() {
let availableSeedOpponentSpot = availableSeedOpponentSpot(inRoundIndex: roundIndex) return seedGroupAvailable(atRoundIndex: roundIndex, availableSeedGroup: availableSeedGroup)
let availableSeeds = seeds(inSeedGroup: seedGroup) } else {
return nil
}
}
func seedGroupAvailable(atRoundIndex roundIndex: Int, availableSeedGroup: SeedInterval) -> SeedInterval? {
if availableSeeds().isEmpty == false && roundIndex >= lastSeedRound() {
if availableSeedGroup == SeedInterval(first: 1, last: 2) { return availableSeedGroup }
let availableSeeds = seeds(inSeedGroup: availableSeedGroup)
let availableSeedSpot = availableSeedSpot(inRoundIndex: roundIndex)
let availableSeedOpponentSpot = availableSeedOpponentSpot(inRoundIndex: roundIndex)
if availableSeeds.count == availableSeedSpot.count {
return availableSeedGroup
} else if (availableSeeds.count == availableSeedOpponentSpot.count && availableSeeds.count == self.availableSeeds().count) {
return availableSeedGroup
} else if let chunk = availableSeedGroup.chunk() {
return seedGroupAvailable(atRoundIndex: roundIndex, availableSeedGroup: chunk)
}
}
if availableSeeds.count <= availableSeedSpot.count { return nil
let spots = availableSeedSpot.shuffled() }
for (index, seed) in availableSeeds.enumerated() {
seed.setSeedPosition(inSpot: spots[index], slot: nil, opposingSeeding: false) func setSeeds(inRoundIndex roundIndex: Int, inSeedGroup seedGroup: SeedInterval) {
if seedGroup == SeedInterval(first: 1, last: 2) {
let seeds = seeds()
if let matches = getRound(atRoundIndex: roundIndex)?.playedMatches() {
if let lastMatch = matches.last {
seeds.prefix(1).first?.setSeedPosition(inSpot: lastMatch, slot: .two, opposingSeeding: false)
}
if let firstMatch = matches.first {
seeds.prefix(2).dropFirst().first?.setSeedPosition(inSpot: firstMatch, slot: .one, opposingSeeding: false)
}
} }
} else if (availableSeeds.count <= availableSeedOpponentSpot.count && availableSeeds.count <= self.availableSeeds().count) { } else {
let spots = availableSeedOpponentSpot.shuffled() let availableSeedSpot = availableSeedSpot(inRoundIndex: roundIndex)
for (index, seed) in availableSeeds.enumerated() { let availableSeedOpponentSpot = availableSeedOpponentSpot(inRoundIndex: roundIndex)
seed.setSeedPosition(inSpot: spots[index], slot: nil, opposingSeeding: true) 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], slot: 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], slot: nil, opposingSeeding: true)
}
} else if let chunk = seedGroup.chunk() {
setSeeds(inRoundIndex: roundIndex, inSeedGroup: chunk)
} }
} else if let chunk = seedGroup.chunk() {
setSeeds(inRoundIndex: roundIndex, inSeedGroup: chunk)
} }
} }

@ -16,11 +16,12 @@ struct SeedInterval: Hashable, Comparable {
} }
func chunk() -> SeedInterval? { func chunk() -> SeedInterval? {
if last - (last - first) / 2 > first { if (last - first) / 2 > 0 {
return SeedInterval(first: first, last: last - (last - first) / 2) if last - (last - first) / 2 > first {
} else { return SeedInterval(first: first, last: last - (last - first) / 2)
return nil }
} }
return nil
} }
} }

@ -46,7 +46,6 @@ struct PlayerBlockView: View {
if match.upperBracketMatch(teamPosition)?.disabled == true { if match.upperBracketMatch(teamPosition)?.disabled == true {
return "Bye" return "Bye"
} }
return teamPosition.localizedLabel() return teamPosition.localizedLabel()
} }

@ -81,22 +81,14 @@ struct LoserRoundView: View {
if _roundDisabled() { if _roundDisabled() {
RowButtonView("Jouer ce tour", role: .destructive) { RowButtonView("Jouer ce tour", role: .destructive) {
loserRounds.forEach { round in loserRounds.forEach { round in
let matches = round.playedMatches() round.enableRound()
matches.forEach { match in round.handleLoserRoundState()
if round.upperBracketTopMatch(ofMatchIndex: match.index)?.disabled == false && round.upperBracketBottomMatch(ofMatchIndex: match.index)?.disabled == false {
match.disabled = false
}
}
try? dataStore.matches.addOrUpdate(contentOfs: matches)
} }
} }
} else { } else {
RowButtonView("Ne pas jouer ce tour", role: .destructive) { RowButtonView("Ne pas jouer ce tour", role: .destructive) {
loserRounds.forEach { round in loserRounds.forEach { round in
round.playedMatches().forEach { match in round.disableRound()
match.disabled = true
}
} }
} }
} }

@ -11,93 +11,17 @@ struct RoundSettingsView: View {
@EnvironmentObject var dataStore: DataStore @EnvironmentObject var dataStore: DataStore
@Environment(\.editMode) private var editMode @Environment(\.editMode) private var editMode
@Environment(Tournament.self) var tournament: Tournament @Environment(Tournament.self) var tournament: Tournament
@State private var roundIndex: Int?
var round: Round? {
guard let roundIndex else { return nil }
return tournament.rounds()[roundIndex]
}
var body: some View { var body: some View {
List { List {
if let availableSeedGroup = tournament.availableSeedGroup() {
Section {
Picker(selection: $roundIndex) {
Text("choisir de la manche").tag(nil as Int?)
ForEach(tournament.rounds()) { round in
Text(round.roundTitle()).tag(round.index as Int?)
}
} label: {
Text(availableSeedGroup.localizedLabel())
}
if let roundIndex {
RowButtonView("Valider") {
if availableSeedGroup == SeedInterval(first: 1, last: 2) {
let seeds = tournament.seeds()
// let startIndex = RoundRule.matchIndex(fromRoundIndex: roundIndex)
// let numberOfMatchInRound = RoundRule.numberOfMatches(forRoundIndex: roundIndex)
// let lastIndex = startIndex + numberOfMatchInRound - 1
// seeds.prefix(1).first?.bracketPosition = lastIndex * 2 + 1 //TS 1 branche du bas du dernier match
// seeds.prefix(2).dropFirst().first?.bracketPosition = startIndex * 2 //TS 2 branche du haut du premier match
if let matches = tournament.getRound(atRoundIndex: roundIndex)?.playedMatches() {
if let lastMatch = matches.last {
seeds.prefix(1).first?.setSeedPosition(inSpot: lastMatch, slot: .two, opposingSeeding: false)
}
if let firstMatch = matches.first {
seeds.prefix(2).dropFirst().first?.setSeedPosition(inSpot: firstMatch, slot: .one, opposingSeeding: false)
}
}
try? dataStore.teamRegistrations.addOrUpdate(contentOfs: seeds)
} else {
tournament.setSeeds(inRoundIndex: roundIndex, inSeedGroup: availableSeedGroup)
try? dataStore.teamRegistrations.addOrUpdate(contentOfs: tournament.seeds())
}
if tournament.availableSeeds().isEmpty {
editMode?.wrappedValue = .inactive
}
}
}
} header: {
Text("Placement des têtes de série")
}
}
//
// Section {
// RowButtonView("Effacer classement", role: .destructive) {
// tournament.rounds().forEach { round in
// try? dataStore.rounds.delete(contentOfs: round.loserRounds())
// }
// }
// }
//
// Section {
// RowButtonView("Match de classement") {
// tournament.rounds().forEach { round in
// round.buildLoserBracket()
// }
// }
// }
Section { Section {
RowButtonView("Retirer toutes les têtes de séries", role: .destructive) { RowButtonView("Retirer toutes les têtes de séries", role: .destructive) {
tournament.unsortedTeams().forEach({ $0.bracketPosition = nil }) tournament.unsortedTeams().forEach({ $0.bracketPosition = nil })
try? dataStore.teamRegistrations.addOrUpdate(contentOfs: tournament.unsortedTeams()) try? dataStore.teamRegistrations.addOrUpdate(contentOfs: tournament.unsortedTeams())
tournament.allRounds().forEach({ round in tournament.allRounds().forEach({ round in
round.enableRound() round.enableRound()
}) })
} editMode?.wrappedValue = .active
}
Section {
if let lastRound = tournament.rounds().first { // first is final, last round
RowButtonView("Supprimer " + lastRound.roundTitle(), role: .destructive) {
try? dataStore.rounds.delete(instance: lastRound)
}
} }
} }
@ -116,6 +40,13 @@ struct RoundSettingsView: View {
} }
} }
Section {
if let lastRound = tournament.rounds().first { // first is final, last round
RowButtonView("Supprimer " + lastRound.roundTitle(), role: .destructive) {
try? dataStore.rounds.delete(instance: lastRound)
}
}
}
} }
} }
} }

@ -10,6 +10,7 @@ import SwiftUI
struct RoundView: View { struct RoundView: View {
@Environment(\.editMode) private var editMode @Environment(\.editMode) private var editMode
@Environment(Tournament.self) var tournament: Tournament @Environment(Tournament.self) var tournament: Tournament
@EnvironmentObject var dataStore: DataStore
var round: Round var round: Round
@ -29,6 +30,16 @@ struct RoundView: View {
} }
} }
} }
} else if let availableSeedGroup = tournament.seedGroupAvailable(atRoundIndex: round.index) {
RowButtonView("Placer \(availableSeedGroup.localizedLabel())") {
tournament.setSeeds(inRoundIndex: round.index, inSeedGroup: availableSeedGroup)
try? dataStore.teamRegistrations.addOrUpdate(contentOfs: tournament.seeds())
if tournament.availableSeeds().isEmpty {
editMode?.wrappedValue = .inactive
}
}
} }
ForEach(round.playedMatches()) { match in ForEach(round.playedMatches()) { match in

Loading…
Cancel
Save