功能:手動分組改為跨區平衡配對,偶數總人數不再補輪空
根本原因: 原本配對只以人數多的一區為基準,缺額全部補「輪空」。例如 A 區 7 人、B 區 5 人會補 2 個輪空,但總人數 12 是偶數,其實可以讓 A 區 1 人跨區支援就湊滿 6 隊真人對打。 影響: 兩區差距 2 以上時,每輪自動抽「差距÷2」人跨區支援,且每輪輪替不同人,跨區負擔平均分攤;只有總人數為奇數時才會補 1 個輪空。等人數時行為不變,三輪之間照舊旋轉配對讓搭檔不同。 修法: 自 badminton-match-hub 8cc0518 移植 balanceZones / pickCrossMembers,buildManualGroups 改為每輪先平衡兩區再建隊伍,補位改在平衡後只補至多 1 個。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+39
-5
@@ -22,13 +22,18 @@ export function parseRoster(input: string) {
|
||||
}
|
||||
|
||||
export function buildManualGroups(areaA: string[], areaB: string[]) {
|
||||
const targetCount = Math.max(areaA.length, areaB.length)
|
||||
const shuffledA = shuffleList(areaA)
|
||||
const shuffledB = shuffleList(areaB)
|
||||
const paddedA = padTeams(shuffledA, targetCount)
|
||||
const paddedB = padTeams(shuffledB, targetCount)
|
||||
|
||||
return Array.from({ length: TOTAL_GROUPS }, (_, roundIndex) => ({
|
||||
// 每輪先做跨區平衡再配對:兩區差距 2 以上時抽「差距÷2」人跨區支援,
|
||||
// 且每輪輪替不同人;只有總人數為奇數時才會補 1 個輪空。
|
||||
return Array.from({ length: TOTAL_GROUPS }, (_, roundIndex) => {
|
||||
const balanced = balanceZones(shuffledA, shuffledB, roundIndex)
|
||||
const targetCount = Math.max(balanced.areaA.length, balanced.areaB.length)
|
||||
const paddedA = padTeams(balanced.areaA, targetCount)
|
||||
const paddedB = padTeams(balanced.areaB, targetCount)
|
||||
|
||||
return {
|
||||
id: roundIndex + 1,
|
||||
teams: paddedA.map((playerA, index) =>
|
||||
createTeam(
|
||||
@@ -37,7 +42,36 @@ export function buildManualGroups(areaA: string[], areaB: string[]) {
|
||||
paddedB[(index + roundIndex) % paddedB.length],
|
||||
),
|
||||
),
|
||||
}))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function balanceZones(areaA: string[], areaB: string[], roundIndex: number) {
|
||||
const moveCount = Math.floor(Math.abs(areaA.length - areaB.length) / 2)
|
||||
|
||||
if (moveCount === 0) {
|
||||
return { areaA, areaB }
|
||||
}
|
||||
|
||||
if (areaA.length > areaB.length) {
|
||||
const { moved, remaining } = pickCrossMembers(areaA, moveCount, roundIndex)
|
||||
return { areaA: remaining, areaB: [...areaB, ...moved] }
|
||||
}
|
||||
|
||||
const { moved, remaining } = pickCrossMembers(areaB, moveCount, roundIndex)
|
||||
return { areaA: [...areaA, ...moved], areaB: remaining }
|
||||
}
|
||||
|
||||
// 依輪次輪替抽出要跨區支援的人,讓跨區的負擔平均分攤在不同人身上。
|
||||
function pickCrossMembers(list: string[], moveCount: number, roundIndex: number) {
|
||||
const movedIndexes = new Set(
|
||||
Array.from({ length: moveCount }, (_, offset) => (roundIndex * moveCount + offset) % list.length),
|
||||
)
|
||||
|
||||
return {
|
||||
moved: list.filter((_, index) => movedIndexes.has(index)),
|
||||
remaining: list.filter((_, index) => !movedIndexes.has(index)),
|
||||
}
|
||||
}
|
||||
|
||||
export function convertDbRecordToGroups(record: MatchResultsRecord) {
|
||||
|
||||
Reference in New Issue
Block a user