プレイヤーのスキルを測定することは別のトピックであり、私は何も考えません。既存のスキーム (例えば、ELO や Microsoft の研究で開発された公式http://en.wikipedia.org/wiki/TrueSkillや他の多くのスキームの 1 つ) を使用することは、良い出発点だと思います。
マジック ナンバーを取得したら、あなた (およびプレイヤー) の好みに関して、多くの考慮事項が入ります。これは C++ で書かれていませんが、以下にマッチメイキング システムのミニ プロトタイプを示します (100 行の f# コード、ツールをダウンロードせずにhttp://www.tryfsharp.org/Createで操作できます)。
私の設計目標は次のとおりです。
- 100,000 人のプレイヤーがいて、そのうちの数百人がキューにいて、50 ~ 100 のゲームに割り当てられて開始される可能性があることを考えると、それを高速に実行します (チームのバランスを改善するための長い反復はありません)。良い考えではないかもしれません。しかし、私はそれを実験的なフレームワークと考えており、改善/アイデアを配置できる機能は「ImproveTeams」と呼ばれています。
- 特定の時間に開始される複数の新しいゲームを最適化しようとしないでください。
- まったくの初心者とプロゲーマーを同じようにチームに配置しないことで、プレーヤーに自分のスキルレベルでゲーム体験を提供するようにしてください.
使い方:
- プールはプレイヤーのレーティングの降順に並べ替えられます。
- トップ ダウン (最高のプレーヤーから悪いプレーヤーへ) では、上位 2 N (N = チームあたりのプレーヤーの数) を接合するだけでチームが編成されます。これを行うと、チーム A は常にチーム B よりも優れているか、同等に強いという結果になります。
- チーム a、チーム b、およびプールの残りの情報を使用して、ImproveTeams を呼び出します。ImprovementTeams は両方のチームを繰り返し、スキル デルタを計算し、それが正数か負数かに応じて、両方のチームの配列で同じ位置にいるプレーヤーをシャッフルします。
最初のマッチがペアリングされた後、サーバーの容量 (無料のゲーム スロット) が使い果たされるか、プレイヤー プールが空になるまで、プールに残っているプレイヤーにも同じことが適用されます。
欠点: 悪いプレイヤーは常に最後に処理されるため、待ち時間が長くなります。上に示したアルゴリズムをトップダウンで動作させ、デュアル ソリューションをボトムアップで動作させるだけで、簡単な変更で改善できます。
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