import { Player } from "./Player"; interface PairingResult { playerId: string; opponentId: string; isResting: boolean; // 是否为轮空玩家 } class Matchmaker { private mPlayers: Map; private currentRound: number = 0; private restingHistory: Map = new Map(); // 记录每个玩家的轮空次数 constructor(players: Map) { this.mPlayers = players; } /** * 为当前轮次生成配对 * @returns 配对结果数组 */ public generatePairings(): PairingResult[] { const playerIds = Array.from(this.mPlayers.keys()); // 只使用当前存活的玩家 const playerCount = playerIds.length; if (playerCount === 0) { return []; } if (playerCount === 1) { // 只有一个玩家时,与自己配对(轮空) return [{ playerId: playerIds[0], opponentId: playerIds[0], isResting: true }]; } const pairings = this.createRandomPairings(playerIds); this.currentRound++; return pairings; } /** * 创建随机配对 - 使用 Fisher-Yates 洗牌算法 * @param playerIds 玩家ID数组 * @returns 配对结果 */ private createRandomPairings(playerIds: string[]): PairingResult[] { const playerCount = playerIds.length; const isOddCount = playerCount % 2 === 1; // 如果是奇数,需要找一个玩家轮空 let restingPlayer: string | null = null; let activePlayers = [...playerIds]; if (isOddCount) { restingPlayer = this.selectRestingPlayer(playerIds); activePlayers = playerIds.filter(id => id !== restingPlayer); } // Fisher-Yates 洗牌算法 - O(n) 时间复杂度 const shuffled = this.shuffleArray(activePlayers); const pairings: PairingResult[] = []; // 按顺序两两配对 - O(n) 时间复杂度 for (let i = 0; i < shuffled.length; i += 2) { if (i + 1 < shuffled.length) { const player1 = shuffled[i]; const player2 = shuffled[i + 1]; pairings.push({ playerId: player1, opponentId: player2, isResting: false }); pairings.push({ playerId: player2, opponentId: player1, isResting: false }); } } // 处理轮空玩家 if (restingPlayer) { const ghostOpponent = this.selectGhostOpponent(restingPlayer, playerIds); pairings.push({ playerId: restingPlayer, opponentId: ghostOpponent, isResting: true }); } return pairings; } /** * Fisher-Yates 洗牌算法 - 完全随机且高效 * @param array 要洗牌的数组 * @returns 洗牌后的新数组 */ private shuffleArray(array: T[]): T[] { const result = [...array]; for (let i = result.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [result[i], result[j]] = [result[j], result[i]]; } return result; } /** * 选择轮空玩家(轮换制,确保每个玩家都有机会轮空) * @param playerIds 玩家ID数组(当前存活的玩家) * @returns 轮空玩家ID */ private selectRestingPlayer(playerIds: string[]): string { // 初始化轮空次数记录 playerIds.forEach(id => { if (!this.restingHistory.has(id)) { this.restingHistory.set(id, 0); } }); // 找到轮空次数最少的玩家 let minRestingCount = Infinity; playerIds.forEach(id => { const count = this.restingHistory.get(id) || 0; if (count < minRestingCount) { minRestingCount = count; } }); const candidatePlayers = playerIds.filter(id => { const count = this.restingHistory.get(id) || 0; return count === minRestingCount; }); // 在轮空次数最少的玩家中随机选择 const selectedIndex = Math.floor(Math.random() * candidatePlayers.length); const selectedPlayer = candidatePlayers[selectedIndex]; // 增加该玩家的轮空次数 this.restingHistory.set(selectedPlayer, (this.restingHistory.get(selectedPlayer) || 0) + 1); return selectedPlayer; } /** * 为轮空玩家选择一个"幽灵对手"(用于显示) * @param restingPlayer 轮空玩家ID * @param allPlayers 所有当前存活的玩家ID * @returns 幽灵对手ID */ private selectGhostOpponent(restingPlayer: string, allPlayers: string[]): string { // 只在当前存活的玩家中选择幽灵对手 const alivePlayers = allPlayers.filter(id => id !== restingPlayer); if (alivePlayers.length === 0) { return restingPlayer; // 如果没有其他存活玩家,返回自己 } // 随机选择一个幽灵对手 const randomIndex = Math.floor(Math.random() * alivePlayers.length); return alivePlayers[randomIndex]; } /** * 重置配对历史(新游戏开始时调用) */ public resetHistory(): void { this.restingHistory.clear(); this.currentRound = 0; } /** * 更新玩家列表(当有玩家加入或退出时调用) * @param newPlayers 新的玩家Map */ public updatePlayers(newPlayers: Map): void { this.mPlayers = newPlayers; // 清理已死亡玩家的轮空历史记录 const alivePlayerIds = new Set(newPlayers.keys()); for (const playerId of this.restingHistory.keys()) { if (!alivePlayerIds.has(playerId)) { this.restingHistory.delete(playerId); } } } public deletePlayer(playerId: string) { if (this.mPlayers.has(playerId)) { this.mPlayers.delete(playerId); } // 清理该玩家的轮空历史 this.restingHistory.delete(playerId); } } // 使用示例和辅助函数 function createMatchmaker(mPlayers: Map): Matchmaker { return new Matchmaker(mPlayers); } // 显示配对结果的辅助函数 function displayPairings(pairings: PairingResult[]): void { console.log(`=== 第${pairings.length > 0 ? '当前' : '0'}轮配对结果 ===`); const processedPairs = new Set(); pairings.forEach(pairing => { const pairKey = [pairing.playerId, pairing.opponentId].sort().join('-'); if (!processedPairs.has(pairKey)) { console.log(`${pairing.playerId} VS ${pairing.opponentId}`); processedPairs.add(pairKey); } }); } export { Matchmaker, PairingResult, createMatchmaker, displayPairings };