|
|
|
|
@ -18,12 +18,10 @@ struct TimeMatch { |
|
|
|
|
let matchID: String |
|
|
|
|
let rotationIndex: Int |
|
|
|
|
var courtIndex: Int |
|
|
|
|
let groupIndex: Int |
|
|
|
|
var startDate: Date |
|
|
|
|
var durationLeft: Int //in minutes |
|
|
|
|
var minimumBreakTime: Int //in minutes |
|
|
|
|
var courtLocked: Bool = false |
|
|
|
|
var freeCourt: Bool = false |
|
|
|
|
|
|
|
|
|
func estimatedEndDate(includeBreakTime: Bool) -> Date { |
|
|
|
|
let minutesToAdd = Double(durationLeft + (includeBreakTime ? minimumBreakTime : 0)) |
|
|
|
|
return startDate.addingTimeInterval(minutesToAdd * 60.0) |
|
|
|
|
@ -41,7 +39,6 @@ struct MatchDispatcher { |
|
|
|
|
let timedMatches: [TimeMatch] |
|
|
|
|
let freeCourtPerRotation: [Int: [Int]] |
|
|
|
|
let rotationCount: Int |
|
|
|
|
let groupLastRotation: [Int: Int] |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
extension Match { |
|
|
|
|
@ -59,7 +56,7 @@ class MatchScheduler { |
|
|
|
|
|
|
|
|
|
func groupStageDispatcher(numberOfCourtsAvailablePerRotation: Int, groupStages: [GroupStage], startingDate: Date?, randomizeCourts: Bool) -> GroupStageMatchDispatcher { |
|
|
|
|
|
|
|
|
|
let _groupStages = groupStages.filter { startingDate == nil || $0.startDate == startingDate } |
|
|
|
|
let _groupStages = groupStages |
|
|
|
|
|
|
|
|
|
// Get the maximum count of matches in any group |
|
|
|
|
let maxMatchesCount = _groupStages.map { $0._matches().count }.max() ?? 0 |
|
|
|
|
@ -176,19 +173,6 @@ class MatchScheduler { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func getAvailableCourt(inSlots slots: [TimeMatch], nextStartDate: Date) -> [TimeMatch] { |
|
|
|
|
guard let minimumDuration = slots.compactMap({ $0.durationLeft }).min() else { return [] } |
|
|
|
|
var newSlots = [TimeMatch]() |
|
|
|
|
slots.forEach { timeMatch in |
|
|
|
|
let durationLeft = timeMatch.durationLeft |
|
|
|
|
if durationLeft - minimumDuration > 0 { |
|
|
|
|
let timeMatch = TimeMatch(matchID: timeMatch.matchID, rotationIndex: timeMatch.rotationIndex + 1, courtIndex: timeMatch.courtIndex, groupIndex: timeMatch.groupIndex, startDate: nextStartDate, durationLeft: durationLeft, minimumBreakTime: timeMatch.minimumBreakTime, courtLocked: true) |
|
|
|
|
newSlots.append(timeMatch) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return newSlots |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func getNextStartDate(fromPreviousRotationSlots slots: [TimeMatch], includeBreakTime: Bool) -> Date? { |
|
|
|
|
slots.map { $0.estimatedEndDate(includeBreakTime: includeBreakTime) }.min() |
|
|
|
|
} |
|
|
|
|
@ -207,37 +191,21 @@ class MatchScheduler { |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func roundDispatcher(numberOfCourtsAvailablePerRotation: Int, flattenedMatches: [Match], randomizeCourts: Bool, initialOccupiedCourt: Int = 0, dispatcherStartDate: Date) -> MatchDispatcher { |
|
|
|
|
func roundDispatcher(numberOfCourtsAvailablePerRotation: Int, flattenedMatches: [Match], randomizeCourts: Bool, dispatcherStartDate: Date) -> MatchDispatcher { |
|
|
|
|
|
|
|
|
|
var slots = [TimeMatch]() |
|
|
|
|
var availableMatchs = flattenedMatches |
|
|
|
|
var rotationIndex = 0 |
|
|
|
|
var freeCourtPerRotation = [Int: [Int]]() |
|
|
|
|
var groupLastRotation = [Int: Int]() |
|
|
|
|
var courts = [Int]() |
|
|
|
|
var timeToAdd = 0.0 |
|
|
|
|
|
|
|
|
|
while availableMatchs.count > 0 { |
|
|
|
|
freeCourtPerRotation[rotationIndex] = [] |
|
|
|
|
var matchPerRound = [Int: Int]() |
|
|
|
|
var availableCourt = numberOfCourtsAvailablePerRotation |
|
|
|
|
if rotationIndex == 0 { |
|
|
|
|
availableCourt = availableCourt - initialOccupiedCourt |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
courts = (0..<availableCourt).map { $0 } |
|
|
|
|
|
|
|
|
|
let previousRotationSlots = slots.filter({ $0.rotationIndex == rotationIndex - 1 }) |
|
|
|
|
var rotationStartDate: Date = getNextStartDate(fromPreviousRotationSlots: previousRotationSlots, includeBreakTime: false) ?? dispatcherStartDate |
|
|
|
|
|
|
|
|
|
// let duplicatedSlots = getAvailableCourt(inSlots: previousRotationSlots, nextStartDate: rotationStartDate) |
|
|
|
|
// print("duplicatedSlots", duplicatedSlots) |
|
|
|
|
// slots.append(contentsOf: duplicatedSlots) |
|
|
|
|
// courts.removeAll(where: { index in |
|
|
|
|
// duplicatedSlots.anySatisfy { $0.courtIndex == index } |
|
|
|
|
// }) |
|
|
|
|
|
|
|
|
|
courts.sort() |
|
|
|
|
print("courts available at rotation \(rotationIndex)", courts) |
|
|
|
|
print("rotationStartDate", rotationStartDate) |
|
|
|
|
@ -270,35 +238,24 @@ class MatchScheduler { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// if previousRotationSlots.isEmpty && rotationIndex > 0 { |
|
|
|
|
// let previousPreviousRotationSlots = slots.filter({ $0.rotationIndex == rotationIndex - 2 }) |
|
|
|
|
// rotationStartDate = getNextStartDate(fromPreviousRotationSlots: previousPreviousRotationSlots, includeBreakTime: false) ?? dispatcherStartDate |
|
|
|
|
// } else if freeCourtPreviousRotation > 0 { |
|
|
|
|
// print("scenario where we are waiting for a breaktime to be over without any match to play in between or a free court was available and we need to recheck breaktime left on it") |
|
|
|
|
// let previousPreviousRotationSlots = slots.filter({ $0.rotationIndex == rotationIndex - 2 }) |
|
|
|
|
// if let previousEndDate = getNextStartDate(fromPreviousRotationSlots: previousPreviousRotationSlots, includeBreakTime: true) { |
|
|
|
|
// rotationStartDate = previousEndDate |
|
|
|
|
// courts = freeCourtPerRotation[rotationIndex - 1]! |
|
|
|
|
// } |
|
|
|
|
// } |
|
|
|
|
dispatchCourts(availableCourts: numberOfCourtsAvailablePerRotation, courts: courts, availableMatchs: &availableMatchs, slots: &slots, rotationIndex: rotationIndex, rotationStartDate: rotationStartDate, freeCourtPerRotation: &freeCourtPerRotation) |
|
|
|
|
rotationIndex += 1 |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// var organizedSlots = [TimeMatch]() |
|
|
|
|
// for i in 0..<rotationIndex { |
|
|
|
|
// let courtsSorted = slots.filter({ $0.rotationIndex == i && $0.courtLocked == false }).map { $0.courtIndex }.sorted() |
|
|
|
|
// let courts = randomizeCourts ? courtsSorted.shuffled() : courtsSorted |
|
|
|
|
// var matches = slots.filter({ $0.rotationIndex == i }).sorted(using: .keyPath(\.groupIndex), .keyPath(\.courtIndex)) |
|
|
|
|
// |
|
|
|
|
// for j in 0..<matches.count { |
|
|
|
|
// matches[j].courtIndex = courts[j] |
|
|
|
|
// organizedSlots.append(matches[j]) |
|
|
|
|
// } |
|
|
|
|
// } |
|
|
|
|
// |
|
|
|
|
// |
|
|
|
|
return MatchDispatcher(timedMatches: slots, freeCourtPerRotation: freeCourtPerRotation, rotationCount: rotationIndex, groupLastRotation: groupLastRotation) |
|
|
|
|
var organizedSlots = [TimeMatch]() |
|
|
|
|
for i in 0..<rotationIndex { |
|
|
|
|
let courtsSorted = slots.filter({ $0.rotationIndex == i }).map { $0.courtIndex }.sorted() |
|
|
|
|
let courts = randomizeCourts ? courtsSorted.shuffled() : courtsSorted |
|
|
|
|
var matches = slots.filter({ $0.rotationIndex == i }).sorted(using: .keyPath(\.courtIndex)) |
|
|
|
|
|
|
|
|
|
for j in 0..<matches.count { |
|
|
|
|
matches[j].courtIndex = courts[j] |
|
|
|
|
organizedSlots.append(matches[j]) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return MatchDispatcher(timedMatches: slots, freeCourtPerRotation: freeCourtPerRotation, rotationCount: rotationIndex) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func dispatchCourts(availableCourts: Int, courts: [Int], availableMatchs: inout [Match], slots: inout [TimeMatch], rotationIndex: Int, rotationStartDate: Date, freeCourtPerRotation: inout [Int: [Int]]) { |
|
|
|
|
@ -333,7 +290,7 @@ class MatchScheduler { |
|
|
|
|
matchPerRound[first.roundObject!.index] = 1 |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
let timeMatch = TimeMatch(matchID: first.id, rotationIndex: rotationIndex, courtIndex: courtIndex, groupIndex: first.roundObject!.index, startDate: rotationStartDate, durationLeft: first.matchFormat.estimatedDuration, minimumBreakTime: first.matchFormat.breakTime.breakTime) |
|
|
|
|
let timeMatch = TimeMatch(matchID: first.id, rotationIndex: rotationIndex, courtIndex: courtIndex, startDate: rotationStartDate, durationLeft: first.matchFormat.estimatedDuration, minimumBreakTime: first.matchFormat.breakTime.breakTime) |
|
|
|
|
slots.append(timeMatch) |
|
|
|
|
availableMatchs.removeAll(where: { $0.id == first.id }) |
|
|
|
|
} else { |
|
|
|
|
@ -344,9 +301,6 @@ class MatchScheduler { |
|
|
|
|
if freeCourtPerRotation[rotationIndex]!.count == availableCourts { |
|
|
|
|
freeCourtPerRotation[rotationIndex] = [] |
|
|
|
|
let courtsUsed = getNextEarliestAvailableDate(from: slots) |
|
|
|
|
|
|
|
|
|
print(courtsUsed) |
|
|
|
|
|
|
|
|
|
let freeCourts = courtsUsed.filter { (courtIndex, availableDate) in |
|
|
|
|
availableDate <= minimumTargetedEndDate |
|
|
|
|
}.sorted(by: \.1).map { $0.0 } |
|
|
|
|
@ -380,7 +334,7 @@ class MatchScheduler { |
|
|
|
|
|
|
|
|
|
flattenedMatches.forEach({ $0.startDate = nil }) |
|
|
|
|
|
|
|
|
|
let roundDispatch = self.roundDispatcher(numberOfCourtsAvailablePerRotation: tournament.courtCount, flattenedMatches: flattenedMatches, randomizeCourts: randomizeCourts, initialOccupiedCourt: 0, dispatcherStartDate: startDate) |
|
|
|
|
let roundDispatch = self.roundDispatcher(numberOfCourtsAvailablePerRotation: tournament.courtCount, flattenedMatches: flattenedMatches, randomizeCourts: randomizeCourts, dispatcherStartDate: startDate) |
|
|
|
|
|
|
|
|
|
roundDispatch.timedMatches.forEach { matchSchedule in |
|
|
|
|
if let match = flattenedMatches.first(where: { $0.id == matchSchedule.matchID }) { |
|
|
|
|
|