分享一个动态范围匹配逻辑的实现
import java.util.*; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; public class GameMatchmaking { public static int initialSkillRange = 100; // 初始技能匹配范围 public static int skillRangeExpansion = 100; // 每次扩大的技能范围 public static int expansionPeriod = 1; // 扩大技能范围的周期(秒) public static void main(String[] args) throws Exception { MatchmakingSystem matchmakingSystem = new MatchmakingSystem(5, 10, initialSkillRange); matchmakingSystem.addPlayerToMatchmaking(new Player("PlayerA", 1000)); matchmakingSystem.addPlayerToMatchmaking(new Player("PlayerB", 1200)); matchmakingSystem.addPlayerToMatchmaking(new Player("PlayerC", 1400)); matchmakingSystem.addPlayerToMatchmaking(new Player("PlayerD", 1600)); matchmakingSystem.addPlayerToMatchmaking(new Player("PlayerE", 1800)); matchmakingSystem.addPlayerToMatchmaking(new Player("PlayerF", 2000)); matchmakingSystem.addPlayerToMatchmaking(new Player("PlayerG", 2000)); matchmakingSystem.addPlayerToMatchmaking(new Player("PlayerH", 2000)); matchmakingSystem.addPlayerToMatchmaking(new Player("PlayerI", 2000)); matchmakingSystem.addPlayerToMatchmaking(new Player("PlayerJ", 2000)); matchmakingSystem.addPlayerToMatchmaking(new Player("PlayerK", 12000)); matchmakingSystem.addPlayerToMatchmaking(new Player("PlayerL", 12000)); matchmakingSystem.addPlayerToMatchmaking(new Player("PlayerM", 12000)); matchmakingSystem.addPlayerToMatchmaking(new Player("PlayerN", 12000)); Thread.sleep(30000); matchmakingSystem.addPlayerToMatchmaking(new Player("PlayerO", 12000)); // ... 添加更多玩家 Runtime.getRuntime().addShutdownHook(new Thread(() -> { System.out.println("Shutting down matchmaking system..."); matchmakingSystem.shutdown(); })); // 模拟玩家进入房间后退出 Thread.sleep(5000); // 等待一段时间以允许匹配发生 matchmakingSystem.removePlayerFromMatchmaking(new Player("PlayerC", 1400)); matchmakingSystem.reMatchPlayer(new Player("PlayerC", 1400)); Thread.sleep(5000); // 等待一段时间以允许匹配发生 matchmakingSystem.removePlayerFromMatchmaking(new Player("PlayerC", 1400)); matchmakingSystem.reMatchPlayer(new Player("PlayerC", 1400)); Thread.sleep(5000); // 等待一段时间以允许匹配发生 matchmakingSystem.removePlayerFromMatchmaking(new Player("PlayerC", 1400)); matchmakingSystem.reMatchPlayer(new Player("PlayerC", 1400)); } public static class Player { private final String name; private final int skillLevel; public Player(String name, int skillLevel) { this.name = name; this.skillLevel = skillLevel; } public String getName() { return name; } public int getSkillLevel() { return skillLevel; } @Override public String toString() { return "Player{name='" + name + "', skillLevel=" + skillLevel + '}'; } } public static class Room { private static int nextRoomId = 1; // 静态变量用于生成唯一的房间ID private final int roomId; private final int minPlayers; private final int maxPlayers; private final List<PlayerMatchInfo> players; private int avgSkillLevel; // 用于存储房间平均技能水平 public Room(int minPlayers, int maxPlayers) { this.roomId = nextRoomId++; this.players = new LinkedList<>(); this.minPlayers = minPlayers; this.maxPlayers = maxPlayers; this.avgSkillLevel = 0; } public boolean addPlayer(PlayerMatchInfo playerMatchInfo) { if (players.size() < maxPlayers) { players.add(playerMatchInfo); updateAverageSkillLevel(); System.out.println(playerMatchInfo.getPlayer() + " has joined the room [" + getRoomId() + "]. Current room size: " + players.size() + ", avgSkillLevel is " + this.avgSkillLevel); return true; } return false; } public boolean removePlayer(Player player) { PlayerMatchInfo toRemove = null; for (PlayerMatchInfo pmi : players) { if (Objects.equals(pmi.getPlayer().getName(), player.getName())) { toRemove = pmi; break; } } if (toRemove != null) { players.remove(toRemove); updateAverageSkillLevel(); System.out.println(player.getName() + " has left the room [" + getRoomId() + "]. Current room size: " + players.size()); return true; } return false; } public int getRoomId() { return roomId; } private void updateAverageSkillLevel() { if (players.isEmpty()) { avgSkillLevel = 0; return; } avgSkillLevel = players.stream().mapToInt(pmi -> pmi.getPlayer().getSkillLevel()).sum() / players.size(); } public boolean isPlayerCompatible(PlayerMatchInfo playerMatchInfo) { if (players.isEmpty()) { return true; // 如果房间为空,则任何玩家都可以加入 } return players.stream().allMatch(pmi -> pmi.isWithinRange(playerMatchInfo)); } public boolean isReadyToStart() { return players.size() >= minPlayers; } public boolean isFull() { return players.size() == maxPlayers; } public List<Player> getPlayers() { return players.stream().map(PlayerMatchInfo::getPlayer).collect(Collectors.toList()); } } public static class PlayerMatchInfo { private static final int MAX_SKILL_RANGE = Integer.MAX_VALUE; // 最大技能范围 private final Player player; private final long timeAdded; private int skillRange; private int exitCount; // Track the number of times player exits a room private long lastExitTime; // Track the last exit time private int lastRoomId; // Track the last room player exited public PlayerMatchInfo(Player player, int initialSkillRange) { this.player = player; this.timeAdded = System.currentTimeMillis(); this.skillRange = initialSkillRange; this.exitCount = 0; this.lastExitTime = 0; this.lastRoomId = -1; // Initialize with an invalid room ID } public void updateSkillRange() { var beforeSkillRange = this.skillRange; long timeElapsed = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis() - timeAdded); this.skillRange = (int) Math.min(this.skillRange + skillRangeExpansion * (timeElapsed / expansionPeriod), MAX_SKILL_RANGE); System.out.println("Skill range for " + player.getName() + " has been updated from " + beforeSkillRange + " to " + this.skillRange); } public Player getPlayer() { return player; } public int getSkillRange() { return skillRange; } public int getLastRoomId() { return lastRoomId; } public boolean isWithinRange(PlayerMatchInfo other) { // 技能差异 ≤ 两位的技能容差范围最大值 return Math.abs(player.getSkillLevel() - other.player.getSkillLevel()) <= Math.max(skillRange, other.skillRange); } public void recordExit(int roomId) { if (this.getLastRoomId() == roomId) { this.exitCount++; } else { this.exitCount = 1; } this.lastExitTime = System.currentTimeMillis(); this.lastRoomId = roomId; } public boolean canJoinRoom(Room room) { if (this.getLastRoomId() != room.getRoomId()) { return true; } // 如果玩家在同一个房间退出两次,并且距离上次退出不足一分钟,则不能加入 return this.exitCount < 2 || (System.currentTimeMillis() - this.lastExitTime) > 60000; } } public static class MatchmakingSystem { private final int minTeamSize; private final int maxPlayers; private final ScheduledExecutorService executorService; private final Map<String, PlayerMatchInfo> playerInfoMap = new HashMap<>(); private final Queue<PlayerMatchInfo> matchmakingQueue; private final List<Room> rooms; private final int skillRange; public MatchmakingSystem(int minTeamSize, int maxPlayers, int initialSkillRange) { this.matchmakingQueue = new ConcurrentLinkedQueue<>(); this.rooms = Collections.synchronizedList(new ArrayList<>()); this.minTeamSize = minTeamSize; this.maxPlayers = maxPlayers; this.skillRange = initialSkillRange; this.executorService = Executors.newSingleThreadScheduledExecutor(); executorService.scheduleAtFixedRate(this::tryToMatchPlayers, 0, 1, TimeUnit.SECONDS); // 每秒检查一次 } public PlayerMatchInfo getPlayerMatchInfo(Player player) { return playerInfoMap.computeIfAbsent(player.getName(), k -> new PlayerMatchInfo(player, skillRange)); } public void reMatchPlayer(Player player) { PlayerMatchInfo playerInfo = getPlayerMatchInfo(player); removePlayerFromMatchmaking(player); matchmakingQueue.removeIf(pmi -> Objects.equals(pmi.getPlayer().getName(), player.getName())); matchmakingQueue.add(playerInfo); System.out.println(player.getName() + " is looking for a match..."); tryToMatchPlayers(); } public void removePlayerFromMatchmaking(Player player) { PlayerMatchInfo playerInfo = playerInfoMap.get(player.getName()); if (playerInfo != null) { for (Room room : rooms) { if (room.removePlayer(player)) { playerInfo.recordExit(room.getRoomId()); matchmakingQueue.remove(playerInfo); break; } } } } public void addPlayerToMatchmaking(Player player) { PlayerMatchInfo playerInfo = getPlayerMatchInfo(player); // 添加到匹配队列 matchmakingQueue.add(playerInfo); System.out.println(player.getName() + " is looking for a match..."); tryToMatchPlayers(); } private void tryToMatchPlayers() { if (matchmakingQueue.isEmpty()) { return; } // Update skill ranges matchmakingQueue.forEach(PlayerMatchInfo::updateSkillRange); List<PlayerMatchInfo> playersToMatch = new ArrayList<>(matchmakingQueue); Iterator<PlayerMatchInfo> iterator = playersToMatch.iterator(); while (iterator.hasNext()) { PlayerMatchInfo playerToMatch = iterator.next(); Room suitableRoom = findSuitableRoom(playerToMatch); if (suitableRoom != null) { suitableRoom.addPlayer(playerToMatch); matchmakingQueue.remove(playerToMatch); iterator.remove(); if (suitableRoom.isReadyToStart()) { System.out.println("Room [" + suitableRoom.getRoomId() + "] " + suitableRoom.getPlayers().stream().map(Player::getName).toList() + " is ready to start the game!"); } } } // 如果没有合适的房间,尝试创建新的房间 if (!playersToMatch.isEmpty()) { List<PlayerMatchInfo> potentialTeam = findPotentialTeam(playersToMatch); if (potentialTeam != null) { Room room = new Room(minTeamSize, maxPlayers); potentialTeam.forEach(pmi -> { room.addPlayer(pmi); matchmakingQueue.remove(pmi); }); rooms.add(room); System.out.println("Room " + room.getPlayers().stream().map(Player::getName).toList() + " is ready to start the game!"); } } } private Room findSuitableRoom(PlayerMatchInfo playerToMatch) { for (Room room : rooms) { if (!room.isFull() && // 是否相互匹配 room.isPlayerCompatible(playerToMatch) && // 是否冷却时间内 playerToMatch.canJoinRoom(room)) { return room; } } return null; } private List<PlayerMatchInfo> findPotentialTeam(List<PlayerMatchInfo> players) { if (players.size() < minTeamSize) { return null; } for (int i = 0; i <= players.size() - minTeamSize; i++) { List<PlayerMatchInfo> potentialTeam = players.subList(i, i + minTeamSize); if (isTeamCompatible(potentialTeam)) { return new ArrayList<>(potentialTeam); } } return null; } private boolean isTeamCompatible(List<PlayerMatchInfo> team) { if (team.isEmpty()) { return false; } var avgSkill = team.stream().mapToInt(p -> p.getPlayer().getSkillLevel()).average().orElse(0.0); // 如果当前匹配队列中的玩家匹配值的平均值,减去玩家的技能水平(得到自己与平均值的差异),小于玩家的匹配值容差,则认为是合适的队伍 return team.stream().allMatch(p -> Math.abs(avgSkill - p.getPlayer().getSkillLevel()) <= p.getSkillRange()); } public void shutdown() { executorService.shutdown(); } } }
这里每一位玩家都有一个技能水平skillLevel
,在玩家进入匹配后会随着时间动态扩大匹配范围区间,例如技能水平为1000
的玩家一开始是匹配900~1100
范围区间的玩家,然后过了1
秒变为能匹配到800~1200
范围的玩家
当五位玩家都相互满足范围区间时,这五位玩家组成一个房间
房间会继续接受玩家加入,直到满足最大人数十
这是这个逻辑的日志输出:
Connected to the target VM, address: '127.0.0.1:49327', transport: 'socket' 20:50:14.891 [main] DEBUG reactor.util.Loggers - Using Slf4j logging framework 20:50:14.893 [main] DEBUG reactor.core.publisher.Hooks - Enabling stacktrace debugging via onOperatorDebug PlayerA is looking for a match... Skill range for PlayerA has been updated from 100 to 100 PlayerB is looking for a match... Skill range for PlayerA has been updated from 100 to 100 Skill range for PlayerB has been updated from 100 to 100 PlayerC is looking for a match... Skill range for PlayerA has been updated from 100 to 100 Skill range for PlayerB has been updated from 100 to 100 Skill range for PlayerC has been updated from 100 to 100 PlayerD is looking for a match... Skill range for PlayerA has been updated from 100 to 100 Skill range for PlayerB has been updated from 100 to 100 Skill range for PlayerC has been updated from 100 to 100 Skill range for PlayerD has been updated from 100 to 100 PlayerE is looking for a match... Skill range for PlayerA has been updated from 100 to 100 Skill range for PlayerB has been updated from 100 to 100 Skill range for PlayerC has been updated from 100 to 100 Skill range for PlayerD has been updated from 100 to 100 Skill range for PlayerE has been updated from 100 to 100 PlayerF is looking for a match... Skill range for PlayerA has been updated from 100 to 100 Skill range for PlayerB has been updated from 100 to 100 Skill range for PlayerC has been updated from 100 to 100 Skill range for PlayerD has been updated from 100 to 100 Skill range for PlayerE has been updated from 100 to 100 Skill range for PlayerF has been updated from 100 to 100 PlayerG is looking for a match... Skill range for PlayerA has been updated from 100 to 100 Skill range for PlayerB has been updated from 100 to 100 Skill range for PlayerC has been updated from 100 to 100 Skill range for PlayerD has been updated from 100 to 100 Skill range for PlayerE has been updated from 100 to 100 Skill range for PlayerF has been updated from 100 to 100 Skill range for PlayerG has been updated from 100 to 100 PlayerH is looking for a match... Skill range for PlayerA has been updated from 100 to 100 Skill range for PlayerB has been updated from 100 to 100 Skill range for PlayerC has been updated from 100 to 100 Skill range for PlayerD has been updated from 100 to 100 Skill range for PlayerE has been updated from 100 to 100 Skill range for PlayerF has been updated from 100 to 100 Skill range for PlayerG has been updated from 100 to 100 Skill range for PlayerH has been updated from 100 to 100 PlayerI is looking for a match... Skill range for PlayerA has been updated from 100 to 100 Skill range for PlayerB has been updated from 100 to 100 Skill range for PlayerC has been updated from 100 to 100 Skill range for PlayerD has been updated from 100 to 100 Skill range for PlayerE has been updated from 100 to 100 Skill range for PlayerF has been updated from 100 to 100 Skill range for PlayerG has been updated from 100 to 100 Skill range for PlayerH has been updated from 100 to 100 Skill range for PlayerI has been updated from 100 to 100 PlayerJ is looking for a match... Skill range for PlayerA has been updated from 100 to 100 Skill range for PlayerB has been updated from 100 to 100 Skill range for PlayerC has been updated from 100 to 100 Skill range for PlayerD has been updated from 100 to 100 Skill range for PlayerE has been updated from 100 to 100 Skill range for PlayerF has been updated from 100 to 100 Skill range for PlayerG has been updated from 100 to 100 Skill range for PlayerH has been updated from 100 to 100 Skill range for PlayerI has been updated from 100 to 100 Skill range for PlayerJ has been updated from 100 to 100 Player{name='PlayerF', skillLevel=2000} has joined the room [1]. Current room size: 1, avgSkillLevel is 2000 Player{name='PlayerG', skillLevel=2000} has joined the room [1]. Current room size: 2, avgSkillLevel is 2000 Player{name='PlayerH', skillLevel=2000} has joined the room [1]. Current room size: 3, avgSkillLevel is 2000 Player{name='PlayerI', skillLevel=2000} has joined the room [1]. Current room size: 4, avgSkillLevel is 2000 Player{name='PlayerJ', skillLevel=2000} has joined the room [1]. Current room size: 5, avgSkillLevel is 2000 Room [PlayerF, PlayerG, PlayerH, PlayerI, PlayerJ] is ready to start the game! PlayerK is looking for a match... Skill range for PlayerA has been updated from 100 to 100 Skill range for PlayerB has been updated from 100 to 100 Skill range for PlayerC has been updated from 100 to 100 Skill range for PlayerD has been updated from 100 to 100 Skill range for PlayerE has been updated from 100 to 100 Skill range for PlayerK has been updated from 100 to 100 PlayerL is looking for a match... Skill range for PlayerA has been updated from 100 to 100 Skill range for PlayerB has been updated from 100 to 100 Skill range for PlayerC has been updated from 100 to 100 Skill range for PlayerD has been updated from 100 to 100 Skill range for PlayerE has been updated from 100 to 100 Skill range for PlayerK has been updated from 100 to 100 Skill range for PlayerL has been updated from 100 to 100 PlayerM is looking for a match... Skill range for PlayerA has been updated from 100 to 100 Skill range for PlayerB has been updated from 100 to 100 Skill range for PlayerC has been updated from 100 to 100 Skill range for PlayerD has been updated from 100 to 100 Skill range for PlayerE has been updated from 100 to 100 Skill range for PlayerK has been updated from 100 to 100 Skill range for PlayerL has been updated from 100 to 100 Skill range for PlayerM has been updated from 100 to 100 PlayerN is looking for a match... Skill range for PlayerA has been updated from 100 to 100 Skill range for PlayerB has been updated from 100 to 100 Skill range for PlayerC has been updated from 100 to 100 Skill range for PlayerD has been updated from 100 to 100 Skill range for PlayerE has been updated from 100 to 100 Skill range for PlayerK has been updated from 100 to 100 Skill range for PlayerL has been updated from 100 to 100 Skill range for PlayerM has been updated from 100 to 100 Skill range for PlayerN has been updated from 100 to 100 Skill range for PlayerA has been updated from 100 to 200 Skill range for PlayerB has been updated from 100 to 100 Skill range for PlayerC has been updated from 100 to 100 Skill range for PlayerD has been updated from 100 to 100 Skill range for PlayerE has been updated from 100 to 100 Skill range for PlayerK has been updated from 100 to 100 Skill range for PlayerL has been updated from 100 to 100 Skill range for PlayerM has been updated from 100 to 100 Skill range for PlayerN has been updated from 100 to 100 Skill range for PlayerA has been updated from 200 to 400 Skill range for PlayerB has been updated from 100 to 200 Skill range for PlayerC has been updated from 100 to 200 Skill range for PlayerD has been updated from 100 to 200 Skill range for PlayerE has been updated from 100 to 200 Skill range for PlayerK has been updated from 100 to 200 Skill range for PlayerL has been updated from 100 to 200 Skill range for PlayerM has been updated from 100 to 200 Skill range for PlayerN has been updated from 100 to 200 Player{name='PlayerE', skillLevel=1800} has joined the room [1]. Current room size: 6, avgSkillLevel is 1966 Room [1] [PlayerF, PlayerG, PlayerH, PlayerI, PlayerJ, PlayerE] is ready to start the game! Skill range for PlayerA has been updated from 400 to 700 Skill range for PlayerB has been updated from 200 to 400 Skill range for PlayerC has been updated from 200 to 400 Skill range for PlayerD has been updated from 200 to 400 Skill range for PlayerK has been updated from 200 to 400 Skill range for PlayerL has been updated from 200 to 400 Skill range for PlayerM has been updated from 200 to 400 Skill range for PlayerN has been updated from 200 to 400 Player{name='PlayerD', skillLevel=1600} has joined the room [1]. Current room size: 7, avgSkillLevel is 1914 Room [1] [PlayerF, PlayerG, PlayerH, PlayerI, PlayerJ, PlayerE, PlayerD] is ready to start the game! Skill range for PlayerA has been updated from 700 to 1100 Skill range for PlayerB has been updated from 400 to 700 Skill range for PlayerC has been updated from 400 to 700 Skill range for PlayerK has been updated from 400 to 700 Skill range for PlayerL has been updated from 400 to 700 Skill range for PlayerM has been updated from 400 to 700 Skill range for PlayerN has been updated from 400 to 700 Player{name='PlayerA', skillLevel=1000} has joined the room [1]. Current room size: 8, avgSkillLevel is 1800 Room [1] [PlayerF, PlayerG, PlayerH, PlayerI, PlayerJ, PlayerE, PlayerD, PlayerA] is ready to start the game! Player{name='PlayerC', skillLevel=1400} has joined the room [1]. Current room size: 9, avgSkillLevel is 1755 Room [1] [PlayerF, PlayerG, PlayerH, PlayerI, PlayerJ, PlayerE, PlayerD, PlayerA, PlayerC] is ready to start the game! Skill range for PlayerB has been updated from 700 to 1100 Skill range for PlayerK has been updated from 700 to 1100 Skill range for PlayerL has been updated from 700 to 1100 Skill range for PlayerM has been updated from 700 to 1100 Skill range for PlayerN has been updated from 700 to 1100 Player{name='PlayerB', skillLevel=1200} has joined the room [1]. Current room size: 10, avgSkillLevel is 1700 Room [1] [PlayerF, PlayerG, PlayerH, PlayerI, PlayerJ, PlayerE, PlayerD, PlayerA, PlayerC, PlayerB] is ready to start the game! Skill range for PlayerK has been updated from 1100 to 1600 Skill range for PlayerL has been updated from 1100 to 1600 Skill range for PlayerM has been updated from 1100 to 1600 Skill range for PlayerN has been updated from 1100 to 1600 Skill range for PlayerK has been updated from 1600 to 2200 Skill range for PlayerL has been updated from 1600 to 2200 Skill range for PlayerM has been updated from 1600 to 2200 Skill range for PlayerN has been updated from 1600 to 2200 Skill range for PlayerK has been updated from 2200 to 2900 Skill range for PlayerL has been updated from 2200 to 2900 Skill range for PlayerM has been updated from 2200 to 2900 Skill range for PlayerN has been updated from 2200 to 2900 Skill range for PlayerK has been updated from 2900 to 3700 Skill range for PlayerL has been updated from 2900 to 3700 Skill range for PlayerM has been updated from 2900 to 3700 Skill range for PlayerN has been updated from 2900 to 3700 Skill range for PlayerK has been updated from 3700 to 4600 Skill range for PlayerL has been updated from 3700 to 4600 Skill range for PlayerM has been updated from 3700 to 4600 Skill range for PlayerN has been updated from 3700 to 4600 Skill range for PlayerK has been updated from 4600 to 5600 Skill range for PlayerL has been updated from 4600 to 5600 Skill range for PlayerM has been updated from 4600 to 5600 Skill range for PlayerN has been updated from 4600 to 5600 Skill range for PlayerK has been updated from 5600 to 6700 Skill range for PlayerL has been updated from 5600 to 6700 Skill range for PlayerM has been updated from 5600 to 6700 Skill range for PlayerN has been updated from 5600 to 6700 Skill range for PlayerK has been updated from 6700 to 7900 Skill range for PlayerL has been updated from 6700 to 7900 Skill range for PlayerM has been updated from 6700 to 7900 Skill range for PlayerN has been updated from 6700 to 7900 Skill range for PlayerK has been updated from 7900 to 9200 Skill range for PlayerL has been updated from 7900 to 9200 Skill range for PlayerM has been updated from 7900 to 9200 Skill range for PlayerN has been updated from 7900 to 9200 Skill range for PlayerK has been updated from 9200 to 10600 Skill range for PlayerL has been updated from 9200 to 10600 Skill range for PlayerM has been updated from 9200 to 10600 Skill range for PlayerN has been updated from 9200 to 10600 Skill range for PlayerK has been updated from 10600 to 12100 Skill range for PlayerL has been updated from 10600 to 12100 Skill range for PlayerM has been updated from 10600 to 12100 Skill range for PlayerN has been updated from 10600 to 12100 Skill range for PlayerK has been updated from 12100 to 13700 Skill range for PlayerL has been updated from 12100 to 13700 Skill range for PlayerM has been updated from 12100 to 13700 Skill range for PlayerN has been updated from 12100 to 13700 Skill range for PlayerK has been updated from 13700 to 15400 Skill range for PlayerL has been updated from 13700 to 15400 Skill range for PlayerM has been updated from 13700 to 15400 Skill range for PlayerN has been updated from 13700 to 15400 Skill range for PlayerK has been updated from 15400 to 17200 Skill range for PlayerL has been updated from 15400 to 17200 Skill range for PlayerM has been updated from 15400 to 17200 Skill range for PlayerN has been updated from 15400 to 17200 Skill range for PlayerK has been updated from 17200 to 19100 Skill range for PlayerL has been updated from 17200 to 19100 Skill range for PlayerM has been updated from 17200 to 19100 Skill range for PlayerN has been updated from 17200 to 19100 Skill range for PlayerK has been updated from 19100 to 21100 Skill range for PlayerL has been updated from 19100 to 21100 Skill range for PlayerM has been updated from 19100 to 21100 Skill range for PlayerN has been updated from 19100 to 21100 Skill range for PlayerK has been updated from 21100 to 23200 Skill range for PlayerL has been updated from 21100 to 23200 Skill range for PlayerM has been updated from 21100 to 23200 Skill range for PlayerN has been updated from 21100 to 23200 Skill range for PlayerK has been updated from 23200 to 25400 Skill range for PlayerL has been updated from 23200 to 25400 Skill range for PlayerM has been updated from 23200 to 25400 Skill range for PlayerN has been updated from 23200 to 25400 Skill range for PlayerK has been updated from 25400 to 27700 Skill range for PlayerL has been updated from 25400 to 27700 Skill range for PlayerM has been updated from 25400 to 27700 Skill range for PlayerN has been updated from 25400 to 27700 Skill range for PlayerK has been updated from 27700 to 30100 Skill range for PlayerL has been updated from 27700 to 30100 Skill range for PlayerM has been updated from 27700 to 30100 Skill range for PlayerN has been updated from 27700 to 30100 Skill range for PlayerK has been updated from 30100 to 32600 Skill range for PlayerL has been updated from 30100 to 32600 Skill range for PlayerM has been updated from 30100 to 32600 Skill range for PlayerN has been updated from 30100 to 32600 Skill range for PlayerK has been updated from 32600 to 35200 Skill range for PlayerL has been updated from 32600 to 35200 Skill range for PlayerM has been updated from 32600 to 35200 Skill range for PlayerN has been updated from 32600 to 35200 Skill range for PlayerK has been updated from 35200 to 37900 Skill range for PlayerL has been updated from 35200 to 37900 Skill range for PlayerM has been updated from 35200 to 37900 Skill range for PlayerN has been updated from 35200 to 37900 Skill range for PlayerK has been updated from 37900 to 40700 Skill range for PlayerL has been updated from 37900 to 40700 Skill range for PlayerM has been updated from 37900 to 40700 Skill range for PlayerN has been updated from 37900 to 40700 Skill range for PlayerK has been updated from 40700 to 43600 Skill range for PlayerL has been updated from 40700 to 43600 Skill range for PlayerM has been updated from 40700 to 43600 Skill range for PlayerN has been updated from 40700 to 43600 PlayerO is looking for a match... Skill range for PlayerK has been updated from 43600 to 46600 Skill range for PlayerL has been updated from 43600 to 46600 Skill range for PlayerM has been updated from 43600 to 46600 Skill range for PlayerN has been updated from 43600 to 46600 Skill range for PlayerO has been updated from 100 to 100 Player{name='PlayerK', skillLevel=12000} has joined the room [2]. Current room size: 1, avgSkillLevel is 12000 Player{name='PlayerL', skillLevel=12000} has joined the room [2]. Current room size: 2, avgSkillLevel is 12000 Player{name='PlayerM', skillLevel=12000} has joined the room [2]. Current room size: 3, avgSkillLevel is 12000 Player{name='PlayerN', skillLevel=12000} has joined the room [2]. Current room size: 4, avgSkillLevel is 12000 Player{name='PlayerO', skillLevel=12000} has joined the room [2]. Current room size: 5, avgSkillLevel is 12000 Room [PlayerK, PlayerL, PlayerM, PlayerN, PlayerO] is ready to start the game! PlayerC has left the room [1]. Current room size: 9 PlayerC is looking for a match... Skill range for PlayerC has been updated from 700 to 4200 Player{name='PlayerC', skillLevel=1400} has joined the room [1]. Current room size: 10, avgSkillLevel is 1700 Room [1] [PlayerF, PlayerG, PlayerH, PlayerI, PlayerJ, PlayerE, PlayerD, PlayerA, PlayerB, PlayerC] is ready to start the game! PlayerC has left the room [1]. Current room size: 9 PlayerC is looking for a match... Skill range for PlayerC has been updated from 4200 to 8200 Skill range for PlayerC has been updated from 8200 to 12200 Player{name='PlayerC', skillLevel=1400} has joined the room [2]. Current room size: 6, avgSkillLevel is 10233 Room [2] [PlayerK, PlayerL, PlayerM, PlayerN, PlayerO, PlayerC] is ready to start the game! PlayerC has left the room [2]. Current room size: 5 PlayerC is looking for a match... Skill range for PlayerC has been updated from 12200 to 16700 Player{name='PlayerC', skillLevel=1400} has joined the room [1]. Current room size: 10, avgSkillLevel is 1700 Room [1] [PlayerF, PlayerG, PlayerH, PlayerI, PlayerJ, PlayerE, PlayerD, PlayerA, PlayerB, PlayerC] is ready to start the game!