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

瑞士轮有两个规则

  1. 选择积分相近的对手进行比赛
  2. 不会重复比赛

写出来的算法如下:

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
}