匹配函数优化:同权重玩家优先同等级匹配后,如何跨等级配对?
玩家匹配函数优化方案
问题背景
原本的玩家匹配函数可实现相同weight的两名玩家配对,逻辑运行正常。现在新增level优先级规则:优先匹配同weight且同level的玩家,但当前函数仅能完成同level玩家配对,无同level的同weight玩家无法被配对。需要优化为:优先同level配对,无同level时允许同weight(或等级相近)的玩家配对。
现有代码(待优化)
const source = [ { entryID: 1, entryName: "player1", weight: 1900, noFight: [], level: 1, }, { entryID: 2, entryName: "player2", weight: 1900, noFight: [], level: 2, }, { entryID: 3, entryName: "player3", weight: 1900, noFight: [], level: 1, }, { entryID: 4, entryName: "player4", weight: 1900, noFight: [], level: 2, }, { entryID: 5, entryName: "player5", weight: 1900, noFight: [], level: 4, }, { entryID: 6, entryName: "player6", weight: 1900, noFight: [], level: 5, }, ]; console.log(combine(source)) function combine(data = [], different = 0, maxGroupSize = 2) { const groups = [] , related = [] , sortedData = [...data].sort((a, b) => a.weight - b.weight) , alreadyInRela = (setX,eName) => { let list = [...setX, eName] return related.some(rela=>list.every(l=>rela.has(l))) }; sortedData.forEach((el,indx)=>{ let place = groups.findIndex( g => g.names.size < maxGroupSize && !g.names.has(el.entryName) && (el.weight - g.weight) <= different && el.level == g.level && !alreadyInRela(g.names, el.entryName ) && !el.noFight.some(noFightEl => g.indxs.map(gi => sortedData[gi].entryID).includes(noFightEl)) ) if (place < 0) { let names = new Set().add(el.entryName) groups.push( { names, indxs: [indx], weight: el.weight, level: el.level } ) related.push( names ) } else { groups[place].names.add(el.entryName) groups[place].indxs.push(indx) } }) return groups.reduce((r,g,i)=>{ if (g.indxs.length > 1) { let key = `${i}_` + g.indxs.map(x=>sortedData[x].weight).join('_') r[key] = [] g.indxs.forEach(x=> r[key].push( sortedData[x]) ) } return r }, {} ) }
当前输出
{ "0_1900_1900": [ { "entryID": 1, "entryName": "player1", "level": 1, "noFight": [], "weight": 1900 }, { "entryID": 3, "entryName": "player3", "level": 1, "noFight": [], "weight": 1900 } ], "1_1900_1900": [ { "entryID": 2, "entryName": "player2", "level": 2, "noFight": [], "weight": 1900 }, { "entryID": 4, "entryName": "player4", "level": 2, "noFight": [], "weight": 1900 } ] }
目标输出
{ "0_1900_1900": [ { "entryID": 1, "entryName": "player1", "level": 1, "noFight": [], "weight": 1900 }, { "entryID": 3, "entryName": "player3", "level": 1, "noFight": [], "weight": 1900 } ], "1_1900_1900": [ { "entryID": 2, "entryName": "player2", "level": 2, "noFight": [], "weight": 1900 }, { "entryID": 4, "entryName": "player4", "level": 2, "noFight": [], "weight": 1900 } ], "2_1900_1900": [ { "entryID": 5, "entryName": "player5", "level": 4, "noFight": [], "weight": 1900 }, { "entryID": 6, "entryName": "player6", "level": 5, "noFight": [], "weight": 1900 } ] }
优化后代码
const source = [ { entryID: 1, entryName: "player1", weight: 1900, noFight: [], level: 1, }, { entryID: 2, entryName: "player2", weight: 1900, noFight: [], level: 2, }, { entryID: 3, entryName: "player3", weight: 1900, noFight: [], level: 1, }, { entryID: 4, entryName: "player4", weight: 1900, noFight: [], level: 2, }, { entryID: 5, entryName: "player5", weight: 1900, noFight: [], level: 4, }, { entryID: 6, entryName: "player6", weight: 1900, noFight: [], level: 5, }, ]; console.log(combine(source)) function combine(data = [], different = 0, maxGroupSize = 2, levelTolerance = 1) { const groups = [] , related = [] , sortedData = [...data].sort((a, b) => a.weight - b.weight) , alreadyInRela = (setX,eName) => { let list = [...setX, eName] return related.some(rela=>list.every(l=>rela.has(l))) }; // 第一步:优先匹配同level的玩家 sortedData.forEach((el,indx)=>{ let place = groups.findIndex( g => g.names.size < maxGroupSize && !g.names.has(el.entryName) && (el.weight - g.weight) <= different && el.level === g.level && !alreadyInRela(g.names, el.entryName ) && !el.noFight.some(noFightEl => g.indxs.map(gi => sortedData[gi].entryID).includes(noFightEl)) ) if (place < 0) { let names = new Set().add(el.entryName) groups.push( { names, indxs: [indx], weight: el.weight, level: el.level } ) related.push( names ) } else { groups[place].names.add(el.entryName) groups[place].indxs.push(indx) } }) // 第二步:提取未配对的玩家,进行等级相近匹配 const unpairedIndices = []; // 先筛选出未完成的组,记录玩家索引并移除这些组 for (let i = groups.length - 1; i >= 0; i--) { if (groups[i].indxs.length < maxGroupSize) { unpairedIndices.push(...groups[i].indxs); groups.splice(i, 1); } } // 处理未配对玩家 const unpairedPlayers = unpairedIndices.map(idx => ({...sortedData[idx], originalIdx: idx})); unpairedPlayers.forEach((el)=>{ let place = groups.findIndex( g => g.names.size < maxGroupSize && !g.names.has(el.entryName) && (el.weight - g.weight) <= different && Math.abs(el.level - g.level) <= levelTolerance // 等级相近判断,可通过参数调整 && !alreadyInRela(g.names, el.entryName ) && !el.noFight.some(noFightEl => g.indxs.map(gi => sortedData[gi].entryID).includes(noFightEl)) ) if (place < 0) { let names = new Set().add(el.entryName) groups.push( { names, indxs: [el.originalIdx], weight: el.weight, level: el.level } ) related.push( names ) } else { groups[place].names.add(el.entryName) groups[place].indxs.push(el.originalIdx) } }) // 生成最终结果 return groups.reduce((r,g,i)=>{ if (g.indxs.length > 1) { let key = `${i}_` + g.indxs.map(x=>sortedData[x].weight).join('_') r[key] = [] g.indxs.forEach(x=> r[key].push( sortedData[x]) ) } return r }, {} ) }
优化说明
- 拆分匹配流程:先完成同level优先匹配,再处理未配对玩家,保证优先级逻辑
- 新增
levelTolerance参数,可自定义等级相近的范围(默认允许1级差距),灵活适配需求 - 保留原有的
weight匹配、noFight黑名单、重复配对校验逻辑,不破坏原有规则 - 提取未完成组的玩家重新匹配,确保无同level的玩家也能找到配对对象
内容的提问来源于stack exchange,提问作者Ulquiorra Schiffer
相关产品推荐
相关产品推荐

