2012-06-12 78 views
1

(這是我正在設計的一款遊戲)可以說,遊戲中有2個玩家團隊。每個球隊將有4名球員。每個玩家都有一個等級(0-9),其中0表示一個壞玩家,9表示一個令人驚歎的玩家。有一個隊列(或列表)的玩家正在等待玩遊戲(這可能是一個小數字或非常大的數字)。可以說每支球隊的總體等級是其中的4名球員的平均水平。有多個可以放置球員的公開比賽和球隊。遊戲的配對算法

問題:什麼是一個好的算法,可以讓玩家進入隊伍中的等待排隊/列表,這樣一個遊戲中的每個隊伍在遊戲中的總體排名會大致相同(不一定非常完美)?另外,玩家不必等待一分鐘以上才能加入團隊(如果玩家人數很少,則可以更多)[放置得越快,越好]

+0

聽起來像加權隊列調度。 你將需要定義「或多或少的整體」,最小二乘法,逆和逆,eliptical,什麼? – starbolin

+0

等待時間是到達率,遊戲數量,遊戲長度和玩家評分分佈的函數。 – starbolin

回答

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

首先,這取決於你如何衡量玩家的技巧。有多種方式可以做到這一點,每種方法都有自己的衡量多個玩家的「平均技能」。

一個好的方法是採用已經開發出來的系統,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的玩家組。同時請記住,玩家羣組所涵蓋的技能範圍應該隨隊列中玩家的數量而變化。

6

這完全取決於團隊的綜合排名有多緊密。如果準確度不那麼重要,你可以做一些簡單的事情:

  1. 把前八名球員從名單中取消。
  2. 排名A隊排名最高,球隊B排名第二
  3. 剩下6名球員,這意味着你剩下20個球隊組合。計算所有20,並選擇導致最接近團隊得分的組合。

這應該是快速和簡單的,但它可能不會產生最均衡的結果。等待時間應該很小,因爲它總是使用等待時間最長的玩家。第2步實際上是消除計算可能性的捷徑。沒有第二步,就有70個可能的團隊組合(「8選4」)。你可能會發現,你可以計算所有的70,並找到一個很好的解決方案,而不佔用太多的時間。提示:理想的球隊得分是(所有球員的總和/ 2)。如果您偶然發現與該特定團隊得分相關的組合,則可以停止。

如果您願意,可以再細化一下。一旦你找到了八強的最佳對位,比較兩隊的比分。如果它們距離太遠(你必須定義什麼構成「太遠」),請將兩個玩家隨機替換爲隊列中的下兩個玩家,然後重試。根據玩家等待的時間長短,你甚至可以讓「太遠」的定義變得更加寬鬆。

你也可以採取稍微不同的方法來解決這個問題。將玩家隨機分組,然後尋找兩個排名相似的團隊(就像比較單個數字一樣簡單)。一旦一支球隊沒有找到一場比賽就花了一定的時間,將這些球員歸還到球隊中,重新組建新球隊。如果你通常有很多隊員排隊(因此有更多的現成球隊),那麼這可能會讓結果更快。

在您花費太多時間在此算法之前,請仔細看看生成玩家排名的算法。人類的技能和經驗不能用一個數字來概括,而沒有相對較大的誤差範圍。如果這裏的錯誤可能相當大,那麼你可以承擔一個不太精確的團隊構建算法(任何額外的準確性將被玩家排名系統中的錯誤所抵消)。

+0

我向您投票贊成「任何額外的準確性將被玩家排名系統中的錯誤取消」。 – starbolin

1

鑑於您的玩家等級數量有限,您可以圍繞此建立算法。有10個隊列,每個隊伍一個。跟蹤每個條目何時被插入,所以你一直知道排名第一的玩家等待最長時間(通過檢查隊列頭i)。

從那裏你可以形成一個遊戲如下。

  1. 以等待時間最長的四個人組成一個團隊。
  2. 獲取他們排名的總T
  3. 迭代T(i,j,k,l)的每個4分區 - 檢查隊列頭i,j,k,l並增加他們的等待時間,找到總共等待時間最長的四人,總等級爲T.將他們組成第二隊並開始比賽。
  4. (如果沒有對步驟3中找到,或者等待(更好的匹配)或擴大搜索[T-Δ,T +δ(更公平的等待時間))

甲整數4- partiioning T是(i,j,k,l)使得i + j + k + l = T。

0

測量玩家技能是一個單獨的話題,我不會給出任何想法。使用現有的方案(例如ELO或Microsoft研究公式http://en.wikipedia.org/wiki/TrueSkill或許多其他方案之一),我認爲這是一個很好的起點。

一旦你有了你的魔法數字,你的(和你的球員)喜好就會有很多考慮因素出現。雖然它不是用C++編寫的,但下面你可以找到我的配對系統的迷你原型(100行f#代碼,你可以在http://www.tryfsharp.org/Create上玩,無需下載任何工具)。

我的設計目標是:

  • 使其運行速度快(無長迭代爲球隊平衡的改善)鑑於有可能值設爲100000名球員,這在排隊幾百,要,分配給大概50-100場比賽即可開始,變得太科學可能不是一個好主意。但我寧願把它看作是一個實驗性框架,你可以將你的改進/想法放入的功能稱爲「改進團隊」。
  • 不要嘗試優化多個新遊戲在特定時間啓動。
  • 嘗試讓玩家在自己的技能水平上獲得遊戲體驗,而不需要爲玩家提供絕對的新手和職業玩家。

工作原理:

  • 游泳池被玩家等級降序排序。
  • 自上而下(最差的球員是糟糕的球員),球隊通過簡單拼接排名前2 N(N =每支球隊的球員數量)進行拼裝。這樣做會導致A隊總是比B隊更好或者同樣強大。
  • 使用team 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 
+0

CreateTeams有一個小錯誤 - 誰可以看到它? ;)提示:如果池的大小小於團隊規模的2倍,它只會出現錯誤。 – BitTickler