1

我有一个应用程序,用户登录以玩快速 1v1 游戏(持续 20 秒)。我想知道将每个用户与另一个用户配对以玩游戏并转移到下一个用户而不连续多次玩同一用户的最有效方法。

我的第一个想法是让两个队列包含每个在线用户的用户 ID。每当有新用户上线时,我都会将他们添加到最短的队列中,并且不断地从每个队列的顶部弹出一个人来互相玩。游戏结束后,我会简单地将每个用户添加到同一个队列中,以避免他们再次互相玩。这看起来不错,但我想看看是否有任何其他更有效的方法来实现这个概念,而无需在服务器上保留以前玩过的用户列表。

4

3 回答 3

1

你的想法行不通。最终(可能很快),你最终会处于这样一个位置,即两个之前玩过的玩家最终排在不同的队列中。你不能保证他们永远不会再被选中一起玩。

想象一下这个简单的案例:

Queue 1       Queue 2
   A            B
   C

A 和 B 玩,并被添加到队列 2:

Queue 1       Queue 2
   C            A
                B

A 和 C 播放,并被添加到队列 1:

Queue 1       Queue 2
   A            B
   C

现在A和B又玩了。C 从来没有机会玩 B。

这个特殊的例子不太可能发生,真的。但是,随着玩家 X 和他玩过的所有玩家之间的距离随着时间的推移而增加,即使队列更大,也会发生类似的事情。在没有与其他所有潜在玩家交手的情况下,两名玩家再次互相交手的可能性非常高。我怀疑概率类似于生日问题

于 2018-07-10T00:02:30.287 回答
1

您需要匹配系统来优先考虑等待时间最长的玩家。

您只需要 1 个队列,还需要使用表格跟踪用户历史记录。如果您想要跨多个会话的永久数据或匹配服务器崩溃,该表可以是临时会话数据或数据库表。该表应该包含他们之前玩过的 playerID 和一组之前的 playerID。最好限制数组的大小并使用 LIFO,因为您可能不想只存储球员最近的比赛,即比赛历史。此外,如果玩家已经在网上与其他所有人进行过比赛,他们可能会用完可以与之对抗的玩家。该表应如下所示:

  • 玩家ID(整数)
  • previousPlayerIDs(整数数组)

当比赛开始时,您可以更新比赛中所有球员的 previousPlayerID。当玩家加入队列时,您需要监听一个事件,让我们调用它 onPlayerJoin()。如果队列中的玩家超过 1 人,您应该选择排队时间最长的玩家,并将他们的 playerID 与每个玩家的 previousPlayerID 进行比较,直到找不到匹配的历史记录。

const historyLimit = 10;

function onPlayerJoin(Player newPlayer){
  playerQueue.push(newPlayer);
  if(playerQueue.length > 1){
    for(let a=0; a<playerQueue.length-1; a++){
      Player player = playerQueue[a];
      for(int i=a+1; i<playerQueue.length; i++){
        Player otherPlayer = playerQueue[i];

        //if the player have not played before
        if(otherPlayer.previousPlayerIDs.indexOf(player.id) > -1){

          //save match up
          player.previousPlayerIDs.push(otherPlayer.id);
          otherPlayer.previousPlayerIDs.push(player.id);

          //limit matchup histroy
          if(player.previousPlayerIDs.length > historyLimit){
            player.previousPlayerIDs.removeAt(0);
          }
          if(otherPlayer.previousPlayerIDs.length > historyLimit){
            otherPlayer.previousPlayerIDs.removeAt(0);
          }

          //create lobby and remove players from the queue
          createLobby(player, otherPlayer);
          playerQueue.removeAt(a);
          playerQueue.removeAt(i);
        }
      }
    }
  }
}

一个玩家有可能和其他人玩过,他们正在等待一个他们以前没有玩过的人上线。您将需要一个重复事件来检查等待时间最长的玩家是否等待太久。如果是这种情况,只需忽略previousPlayerID 的匹配并为该玩家创建一个大厅,并与另一个可能等待很长时间的玩家一起。

如果您愿意,您可以在表中添加更多列,例如他们加入队列时的时间戳及其匹配排名 (elo)。但是,如果您只想优先考虑最近的播放器,则不需要这些其他列。

此外,如果您有大量并发用户,此解决方案可能无法很好地扩展,但如果您的并发用户少于 1,000-10,000,它应该没问题

于 2018-07-10T00:15:43.350 回答
0

您的解决方案似乎很好,但您应该只使用一个队列。

于 2018-07-09T23:20:28.237 回答