游戏的配对算法

时间:2012-06-12 01:12:37

标签: c++ algorithm

(这是我正在设计的游戏)让我们说游戏中有2支队员。每支球队将有4名球员。每个玩家都有一个等级(0-9),其中0表示一个糟糕的玩家,9表示一个惊人的玩家。有一个等待玩游戏的队列(或列表)(这可能是一个小数字或一个非常大的数字)。让我们说每支球队的总体排名是内部4名球员的平均水平。有多个开放式比赛和球队可以放置球员。

问题:什么是一个好的算法,将玩家置于一个团队的等待队列/列表中,以便游戏中的每个团队在游戏中具有或多或少相同的总体等级(不一定是完美的) ?此外,球员不应该等待超过一分钟才能被安排在球队中(如果球员很少,可以更多)[他们的位置越快越好]

5 个答案:

答案 0 :(得分:6)

这完全取决于球队的综合排名需要多少。如果准确性不那么重要,你可以做一些简单的事情:

  1. 将前八名球员从名单中删除。
  2. 在A队排名最高的球员,在B队排名第二
  3. 剩下六名球员,这意味着你剩下20支球队组合。计算所有20并选择最接近团队得分的组合。
  4. 这应该快速而简单,尽管它可能不会产生最平衡的结果。等待时间应该是最小的,因为它总是使用等待时间最长的玩家。第2步实际上是消除计算可能性的捷径。如果没有第2步,则有70种可能的团队组合(“8选4”)。您可能会发现您可以计算所有70并找到一个好的解决方案,而不会占用太多时间。提示:理想的球队得分是(所有球员的总数/ 2)。如果您偶然发现与该特定团队得分的组合,您可以停止。

    如果您愿意,可以进一步完善此步骤。一旦找到八个最佳匹配,请比较两个团队得分。如果他们相距太远(你必须定义什么构成“太远”),随机替换两个玩家,然后再尝试队列中的下两个玩家。你甚至可以根据玩家等待的时间让“太远”的定义变得更加宽松。

    您也可以采取略微不同的方法。将玩家随机分组,然后寻找具有相似排名的两个团队(比较单个数字变得简单)。一旦团队在没有找到匹配的情况下经过了指定的时间,将这些玩家返回到池中以重新组建成新的团队。如果你通常有大量的玩家排队(因此有更多的现成团队),那么这可能会让结果更快。

    在您花费太多时间在此算法上之前,请仔细查看生成播放器排名的算法。如果没有相对较大的误差范围,人类的技能和经验就无法用一个数字来概括。如果此处的错误可能相当大,那么您可以负担得起一个不太准确的团队建设算法(任何额外的准确性都会因玩家排名系统中的错误而无效)。

答案 1 :(得分:2)

你应该开始用一个人建造桌子。如果A人的等级为8,而另一名玩家加入等级为4的游戏,并且您的展示位置指南是2,那么

玩家A拥有该表 玩家A的等级为8

玩家B进入房间 玩家B的等级是否在6&之间? 10?

if (Brank < Arank - 2 || Brank > Arank + 2)

如果确实如此,则排名不在表格的限制范围内,您应该以Brank作为您比较的排名开始一个新表格。

如果这是假的,那么等级是表格声明等级的+ - 2,该玩家可以加入。

如果您真的想要获得幻想,可以根据等待桌子的人数来声明排名。

如果大厅有100人,则限制为+ - 2。 如果在大厅15人,限制+ - 4.(使游戏更加不平衡,但不会导致人们等待那么久)。

答案 2 :(得分:2)

首先,这取决于你如何衡量玩家的技能。有多种方法可以做到这一点,每种方法都有自己的衡量多个玩家“平均技能”的方法。

一种好的方法是采用已经开发的系统,Elo评级(http://en.wikipedia.org/wiki/Elo_rating_system)现在被广泛使用,但是要知道如果你想测量个人玩家的技能,那么简单的实施将不会很好。一个有多个球员的球队。

话虽如此,假设您有一个系统,其中团队的评级是其成员评级的平均值。另外,假设玩家在技能水平之间均匀分布。一个好的第一种方法是在同一游戏中对具有相同技能水平的玩家进行分组。一场比赛,一支球队有2个9评级和2个1评级球员,另一个有4个5评级不会是一个好的。

因此,开始将具有接近技能水平的玩家分组为多个最多8人组。 (玩家可能不止一个)。你可以通过制作1-4级技能球员,2-5,3-6等球员组来做到这一点。当这些球队中有任何一个有8名球员时,你可以将他们安排到比赛中,然后对球队进行排序。一种方式,每个人的平均值大致相同。

现在,存在玩家等待时间过长的问题,因此如果他等待超过1分钟,你可以让技能4的玩家加入技能等级为5-8的玩家组。另请注意,玩家群体所涵盖的技能范围应根据队列中的玩家数量而变化。

答案 3 :(得分:1)

鉴于你有一个有限数量的玩家等级,你可以围绕它建立你的算法。有10个队列,每个队列一个。跟踪每个条目的插入时间,这样你就可以随时知道等待时间最长的玩家是谁(通过检查队列头部i)。

从那里你可以形成如下游戏。

  1. 让等待时间最长的四个人组成一个团队。
  2. 获得他们等级的总T
  3. 遍历T的每个4分区,(i,j,k,l) - 检查队列i,j,k,l的头部并添加他们的等待时间,找到总共等待时间最长的四个人总排名为T.将他们组成第二队并开始比赛。
  4. (如果在步骤3中找不到,请等待(更好地匹配)或将搜索扩展到[T-delta,T + delta](更公平的等待时间))
  5. 整数T的4分区是(i,j,k,l),使得i + j + k + l = T.

答案 4 :(得分:0)

衡量玩家技能是一个单独的主题我不会给出任何想法。使用现有的方案(例如ELO或Microsoft研究开发的公式http://en.wikipedia.org/wiki/TrueSkill或其他许多方案之一)我认为是一个很好的起点。

一旦你拥有了神奇的数字,就会对你(和你的玩家)的偏好产生很多考虑。虽然它不是用C ++编写的,但下面你可以找到我的配对系统的迷你原型(100行f#代码,你可以在http://www.tryfsharp.org/Create上玩,而无需下载任何工具)。

我的设计目标是:

  • 让它快速运行(没有长时间的迭代来改善团队平衡),因为可能有10万名玩家,其中有几百人在队列中,被分配到可能要启动的50-100个游戏,太科学可能不是一个好主意。但我宁愿将其视为一个实验框架,您可以将改进/想法放入其中的功能称为“改进团队”。
  • 不要尝试在给定时间内启动多个新游戏进行优化。
  • 尽量为玩家提供自己技能水平的游戏体验,而不是为绝对新手和职业玩家提供人员配备。

工作原理:

  • 游泳池按玩家等级降序排列。
  • 自上而下(最差球员的最佳球员),球队通过简单拼接前2名N(N =每队球员数量)来组装。这样做会导致A队总是比B队更好或更强。
  • 使用团队a,团队b和池的其余部分的信息调用ImproveTeams。改进团队迭代两个团队,计算技能差异,然后根据是正数还是负数,将两个团队阵列中相同位置的玩家洗牌。

在第一场比赛配对后,同样可以应用于剩余在游泳池中的玩家,直到服务器容量(免费游戏位置)耗尽或玩家池为空。

缺点:糟糕的球员有更长的等待时间,因为他们总是最后处理。一个简单的修改可以通过简单地在上面描述的自上而下的算法与自下而上的双解决方案之间交替来改进。

type Rating = uint32
type RatingDiff = int32
type Player = { name : string; rating : Rating }

// tuple of:  Candidate player in team a, Canditate players in team b, Remainder of pool
type WorkingSet = Player array * Player array * Player array

let pool : Player array = 
    [|  { name = "Hugo"; rating = 1100u }
        { name = "Paul"; rating = 800u }
        { name = "Egon"; rating = 1800u }
        { name = "John"; rating = 1300u }
        { name = "Rob"; rating = 400u }
        { name = "Matt"; rating = 1254u }
        { name = "Bruce"; rating = 2400u }
        { name = "Chuck"; rating = 2286u }
        { name = "Chuck1"; rating = 2186u }
        { name = "Chuck2"; rating = 2860u }
        { name = "Chuck3"; rating = 1286u }
        { name = "Chuck4"; rating = 786u }
    |]

let SortByRating (pool : Player array) : Player array =
    pool 
    |> Array.sortWith 
        (fun (p1 : Player) (p2 : Player) -> 
            match (p1.rating,p2.rating) with 
            | (r1,r2) when r1 > r2 -> -1 
            | (r1,r2) when r1 < r2 -> 1 
            | _ -> 0 
        )

let evens n = 2 * n 
let odds n = 2 * n + 1

// Note: Since the input is sorted by player level, obviously team A is always stronger or equal in strength to team B
let Split (n : int) (a : Player array) : WorkingSet =
    let teamA : Player array = [| for i in 0 .. (n-1) -> a.[evens i] |]
    let teamB : Player array = [| for i in 0 .. (n-1) -> a.[odds i] |]
    let remainder = Array.sub a (2*n) ((Array.length a) - 2 * n)
    ( teamA, teamB, remainder )

 // This is the function where teams get improved - can be as well a recursive function.
 // Anyone is invited to provide alternate, better implementations!
let ImproveTeams (ws : WorkingSet) : WorkingSet =
    let a,b,r = ws
    let R2RD (r : Rating) : RatingDiff =
       let r1 : RatingDiff =  int32 r
       r1
    let UpdateScore (score : RatingDiff) (pa : Player) (pb : Player) : RatingDiff =
        score + (R2RD pa.rating) - (R2RD pb.rating) 
    let improved : RatingDiff * Player array * Player array =
        Array.fold2 
            (fun s pa pb ->
                let score,teamA, teamB = s
                let betterPlayer p1 p2 =
                    match (p1.rating,p2.rating) with
                    | (r1,r2) when r1 >= r2 -> p1
                    | _ -> p2
                let worsePlayer p1 p2 =
                    match (p1.rating,p2.rating) with 
                    | (r1,r2) when r1 >= r2 -> p2
                    | _ -> p1
                let bp = betterPlayer pa pb
                let wp = worsePlayer pa pb
                match score with
                | x when x > 0 -> (UpdateScore x wp bp, Array.append teamA [| wp |], Array.append teamB [| bp |])
                | _ -> (UpdateScore score bp wp, Array.append teamA [| bp |], Array.append teamB [| wp |]) 
             ) (0, [||], [||]) a b
    let ns,nta,ntb = improved
    (nta, ntb,r)    

let rec CreateTeams (maxPlayersPerTeam : int) (players : Player array) : WorkingSet =
    let sortedPool = SortByRating players
    match (Array.length sortedPool) with
    | c when c >= (maxPlayersPerTeam * 2) ->
        Split maxPlayersPerTeam sortedPool
        |> ImproveTeams            
    | 0 -> ( [||], [||], [||] )
    | _ -> CreateTeams (maxPlayersPerTeam-1) players    

let ShowResult (result : WorkingSet) : unit =
    let ShowPlayer p =
        printf "%s - %d" p.name p.rating
    let a,b,r = result
    let Score (pl : Player array) : Rating =
        Array.fold (fun s p -> s + p.rating) 0u pl
    printfn "Team A\t\t\t| Team B"
    Array.iter2 
        ( fun pa pb -> 
            ShowPlayer pa 
            printf "\t\t\t| " 
            ShowPlayer pb
            printfn ""
        ) a b
    let sa = Score a
    let sb = Score b
    printfn "Score Team A: %d\t\t\t| Score Team B: %d" sa sb
    printfn "Players still in pool: "
    Array.iter
        (fun p -> 
            ShowPlayer p
            printfn ""
        ) r

CreateTeams 4 pool |> ShowResult