Go 实现瑞士轮排列算法

工作原因接触到了瑞士轮这种赛制，记录一下瑞士轮比赛对手编排的算法

瑞士轮有两个规则

选择积分相近的对手进行比赛

不会重复比赛

写出来的算法如下:

Copy
type player struct {
Id int64
Score int64
Opponent map[int64]struct{} // 曾经遇到过的对手
}

// pickTablePlayer 计算瑞士轮比赛排列
func pickTablePlayer(players []int64, playerOpponentMap map[int64]map[int64]struct{}) ([]int64, bool) {
if len(players) < 2 {
return players, true
}
whitePlayer := players[0]
opponentMap, _ := playerOpponentMap[whitePlayer]
for i := range players {
if i != 0 {
// 判断是否已经比过
if _, has := opponentMap[players[i]]; !has {
// 选中
res := make([]int64, 2)
res[0] = whitePlayer
res[1] = players[i]

// 组装剩下排序的数据
var nextRound []int64
nextRound = append(nextRound, players[1:i]...)
nextRound = append(nextRound, players[i+1:]...)
pick, ok := pickTablePlayer(nextRound, playerOpponentMap) // 进行下一轮排序
if ok {
return append(res, pick...), true // 成功，结果上浮
}
}
}
}
return nil, false // 失败，上层重算
}

func CreateSwissRound(players []player) (playerBattleList [][]int64, emptyPlayer int64, ok bool) {
ok = true

// 判断轮空选手
total := len(players)
if total%2 != 0 {
emptyPlayer = players[total-1].Id
players = players[:total]
}

// 转换数据结构
var playerIds []int64
var playerOpponentMap = make(map[int64]map[int64]struct{})
for _, v := range players {
playerIds = append(playerIds, v.Id)
if _, has := playerOpponentMap[v.Id]; !has {
playerOpponentMap[v.Id] = v.Opponent
}
}

// 计算比赛排序
playerList, ok := pickTablePlayer(playerIds, playerOpponentMap)
if !ok {
return playerBattleList, emptyPlayer, ok
}

// 转换为二维数组
for i := 0; i < len(playerList)/2; i++ {
playerBattleList = append(playerBattleList, []int64{
playerList[i*2],
playerList[i*2+1],
})
}
return
}