最近利用业余时间开发一个支持多人对战游戏的天梯匹配系统,纯粹练手之用。该天梯系统需要满足以下要求
1. 有单人对战和多人对战模式,例如从1v1到5v5
2. 每个人都有两个天梯分,分别是1v1的天梯分,和2v2或以上对战的天梯分
3. 每局匹配的最高分和最低分玩家分差不能超过设定值
4. 每局匹配双方间分差不能超过设定值
5. 每个人可和一名或多名好友组队共同参与天梯匹配,组队后系统将计算组内天梯值平均分并适量加成作为匹配依据
6. 成功的匹配必须在对战双方同时包含相同的黑店数量。
7. 玩家选择好对战模式后,开始参与天梯匹配,如果匹配成功则返回对阵双方的匹配结果,否则返回空
实现思路概要
1. 匹配池按照<分数,参与玩家/组队列表>的键值对建立红黑树
2. 每一个新玩家/组队参与进来,将会根据对战模式在天梯池寻找合适玩家
3. 匹配过程首先会在天梯池中,选择预选玩家数量 = 对战所需玩家数量 + 一定buffer的玩家数量,选取规则为以当前新玩家分值为起点,分别向高低分数交替选择剩余玩家,直到满足预选玩家数量或者最高分和最低分玩家分差超过设定值
4. 如果预选玩家数量小于对战所需玩家数量 ,则把已选取的玩家和新玩家重新放回匹配池并退出
5. 对预选玩家进行排序,排序依据为队伍大小和新玩家/组队一样,则优先。否则按分数接近程度排列
6. 根据排序结果依次把新玩家和预选玩家交替放入匹配结果的两队中
7. 如果没有在对战双方匹配到的对等的黑店数量,则视为匹配失败
8. 最后根据对战双方天梯分差进行有必要的位置对调,使得两队天梯分总分差最小。
最新代码可从https://github.com/loveisasea/dmatch.git下载
项目采用web形式,可通过http的json请求测试,postman的url为地址。当然也可使用JUnit等工具测试,这就不一一列出了。
示例
其中匹配核心算法如下
package com.fym.match;
import com.fym.core.err.OpException;
import com.fym.core.err.OpResult;
import com.fym.game.enm.GameType;
import com.fym.match.obj.IUnit;
import com.fym.match.obj.Match;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.concurrent.LinkedBlockingQueue;
/**
* Created by fengy on 2016/5/25.
*/
@Component
public class MatchEngine implements InitializingBean {
private static final Logger LOGGER = LoggerFactory.getLogger(MatchEngine.class);
private Map<GameType, TreeMap<Integer, Queue<IUnit>>> matchPools = new HashMap<>();
private final static int Max_Score_Diff = 100;
private final static int Buffer_Match = 4;
public synchronized Match tryMatch(final IUnit mUnit, Integer gameTypeKey) throws OpException {
return this.tryMatch(mUnit, GameType.get(gameTypeKey));
}
/**
* 匹配函数
* 思路:
* 1. 先从参赛者分数震荡向高低两边取未匹配玩家和组队,需要符合最大分数差,而且选取数量带有一定buffer
* 2. 把第一步选取出来的玩家按照规模大小接近和分值接近排序
* 3. 从结果集中
* @param mUnit
* @param gameType
* @return
* @throws OpException
*/
public synchronized Match tryMatch(final IUnit mUnit, GameType gameType) throws OpException {
TreeMap<Integer, Queue<IUnit>> matchpool = this.matchPools.get(gameType);
if (matchpool == null) {
throw new OpExc