We have recently developed a ladder matching system to support multiplayer games in spare time, purely practiced hand. The ladder system needs to meet the following requirements
1. There are single versus multiplayer modes, such as from 1v1 to 5v5
2. Each person has two ladder points, respectively, 1v1 's ladder points, and 2v2 or above the ladder points
3. The maximum and minimum points per game can not exceed the set value
4. The difference between the two sides of each match cannot exceed the set value
5. Each person can join with one or more friends to participate in the ladder matching, the system will be calculated in the group of the ladder value of the average and appropriate addition as a matching basis
6. A successful match must contain the same number of shops in both sides of the PvP.
7. After the player chooses the battle mode, it starts to participate in the ladder match, if the match succeeds, returns the match result to the two sides, otherwise returns the empty
Overview of Implementation Ideas
1. Match pool according to < score, participate in player/Party list > key value pair build red and black tree
2. Each new player/team is involved and will be looking for the right player in the ladder pool according to the PvP mode.
3. The matching process will first be in the ladder pool, select the number of pre-selected players = Number of players required to play + buffer number of players, select the rule for the current new player score as the starting point, respectively, to the high and low scores alternately select the remaining players, until the number of pre-selected players or the highest score and the lowest score of the
4. If the number of pre-selected players is less than the number of players required for PvP, then re-put the chosen player and new player back into the match pool and exit
5. Sort the pre-selected players in the same order as the team size and the new player/team, priority. Otherwise, by the score close to the degree of arrangement
6. According to the sorting results, the new player and the pre-selection player are alternately placed in the two teams matching the results.
7. If there is no equivalent number of shops matched on both sides of the PVP, it is considered a match failure
8. Finally, according to the opposing side of the ladder difference between the necessary position swap, so that the difference between the two teams ranked lowest points.
The latest code can be downloaded from https://github.com/loveisasea/dmatch.git
The project takes the form of web, which can be tested via HTTP JSON request, and the URL of postman is the address. Of course, you can also use tools such as JUnit to test, which is not listed.
Example
Where the matching core algorithm is as follows
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. */@Componentpublic class Matchengine implements Initializingbean {private static final Logger Logger = loggerfactory.g Etlogger (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.trymat CH (munit, Gametype.get (Gametypekey)); }/** * Match function * Idea: * 1. First from the contestants score concussion to the high and low sides to take unmatched players and teams, need to meet the maximum score difference, and select the number with a certain buffer * 2. The players selected in the first step are close to the size and the score is close to sort * 3. From result set * @param munit * @param gametype * @return * @throws opexception */public synchronized Match Trymatch (Final iunit munit, GameType gametype) throws Opexception {Treemap<integer, queue<iunit>> mat Chpool = This.matchPools.get (gametype); if (Matchpool = = null) {throw new Opexception (Opresult.invalid, "the tournament type is not found" + gametype); } if (Munit = = NULL | | munit.getsize () = = 0) {throw new Opexception (Opresult.fail, "match player or team cannot be empty"); } if (Gametype.pcnt < Munit.getsize ()) {throw new Opexception (Opresult.fail, "number of teams exceeding the game type limit"); } int restcnt = gametype.pcnt * 2-munit.getsize () + Buffer_match; Need to pick out the total number of players to match list<iunit> results = new arraylist<> (restcnt); Initial pick up match player//upper/lower score match {Map.entry<integer, Queue<iunit>> hentry = Matchpool.ceilingentry (Munit.getscore ()); Map.entry<integer, queue<iunit>> lentry = Matchpool.floorentry (Munit.getscore ()); Each cycle takes one unit at a time (restcnt > 0) {if (hentry = = NULL && Lentry = = null) { Break }//Search for high player while (hentry! = null && hentry.getvalue (). Size () = = 0) { Hentry = Matchpool.higherentry (Hentry.getkey ()); if (hentry = = null) {break; }} if (hentry! = null) {if (Math.Abs (Hentry.getkey ()-Munit.getscore () ) > Max_score_diff) {hentry = null; } else {Iunit pickunit = Hentry.getvalue (). poll (); if (pickunit! = null) {Results.add (pickunit); restcnt = Restcnt-pickunit.getsize (); }}//Search for low player while (lentry! = null && lentry.ge TValue (). Size () = = 0) {lentry = Matchpool.lowerentry (Lentry.getkey ()); if (lentry = = null) {break; }} if (lentry! = null) {if (Math.Abs (Lentry.getkey ()-Munit.getscore () ) > Max_score_diff) {lentry = null; } else {Iunit pickunit = Lentry.getvalue (). poll (); if (pickunit! = null) {Results.add (pickunit); restcnt = Restcnt-pickunit.getsize (); }}}}} match match = new Match (gametype); List<iunit> Toreturn = results; There are enough players to do twoSub-Select if (restcnt <= buffer_match) {Collections.sort (results, new comparator<iunit> () { Player selection Order rule @Override public int compare (Iunit O1, iunit O2) {int AbsSize1 = Math.Abs (Munit.getsize ()-o1.getsize ()); int absSize2 = Math.Abs (Munit.getsize ()-o2.getsize ()); if (absSize1 = = absSize2) {return-1; } else {if (O1.getscore () < O2.getscore ()) {return-1; } else if (O1.getscore () > O2.getscore ()) {return 1; } else {return 0; } } } }); Start the second selection list<iunit> results2 = new arraylist<> (results); Match.team1.add (Munit); Iunit lastTeam1 = Munit; Iunit lastTeam2 = null; while (true) {//Pick team2, the size required to satisfy the unit equals team1 iterator<iunit> iter2 = Results2.iterator ( ); LASTTEAM2 = null; while (Match.team2size () < gametype.pcnt && Iter2.hasnext ()) {Iunit pickunit = Iter2.next () ; if (pickunit.getsize () = = Lastteam1.getsize ()) {lastTeam2 = Pickunit; Match.team2.add (LASTTEAM2); Iter2.remove (); Break }}//Cannot find team2, exit if (lastTeam2 = = null) {break; }//Pick team1 iterator<iunit> iter1 = Results2.iterator (); LASTTEAM1 = null; while (Match.team1size () < gametype.pcnt && Iter1.hasnext ()) {Iunit pickunit = Iter1.next () ; if (pickunit.getsize () + match.team1size () <= gametype.pcnt) {lastTeam1 = Pickunit; Match.team1.add (LASTTEAM1); Iter1.remove (); Break }}//Cannot find team1, exit if (lastTeam1 = = null) {break; }} if (match.team1size () = = gametype.pcnt && match.team2size () = = gametype.pcnt) { Toreturn = results2; } else {toreturn.add (munit);//need to add the current player in match = null; }} else {Toreturn.add (munit);//need to add the current player in match = null; }//restore iterator<iunit> Ireturn = Toreturn.iterator (); while (Ireturn.hasnext ()) {Iunit picked = Ireturn.next (); queue<iunit> queue = Matchpool.get (Picked.getscore ()); if (queue = = null) { Queue = new linkedblockingqueue<> (); Matchpool.put (Picked.getscore (), queue); } queue.add (picked); } if (match = = null) {return null; } else {//Balance team1 and team2 score match Matchret = new match (Match.gametype); for (int i = 0; i < match.team1.size (); i++) {Boolean team1higher = Matchret.scorediff () > 0; Boolean team2higher = (Match.team2.get (i). Getscore ()-Match.team1.get (i). Getscore ()) > 0; if (team1higher ^ team2higher) {Matchret.team1.add (Match.team2.get (i)); Matchret.team2.add (Match.team1.get (i)); } else {Matchret.team1.add (Match.team1.get (i)); Matchret.team2.add (Match.team2.get (i)); }} return match; }} public synchronized Map<gametype, Map<integer, List<iuniT>>> Getmatchpools () {MAP ret = new HashMap (); For (Map.entry<gametype, Treemap<integer, queue<iunit>>> MapEntry:this.matchPools.entrySet ()) { Hashmap<integer, list<iunit>> retentry = new HashMap (); For (Map.entry<integer, queue<iunit>> entry:mapEntry.getValue (). EntrySet ()) {retentry.put (en Try.getkey (), New arraylist<iunit> (Entry.getvalue ())); } ret.put (Mapentry.getkey (), retentry); } return ret; } public synchronized Map<integer, list<iunit>> getmatchpool (Integer gametypekey) throws Opexception { Treemap<integer, queue<iunit>> matchpool = This.matchPools.get (Gametype.get (Gametypekey)); if (Matchpool = = null) {throw new Opexception (Opresult.invalid, "the tournament type is not found" + gametypekey); } hashmap<integer, list<iunit>> ret = new HashMap (); for (Map.entry<integEr, queue<iunit>> entry:matchpool.entrySet ()) {Ret.put (Entry.getkey (), New Arraylist<iunit> (E Ntry.getvalue ())); } return ret; } public synchronized void Cleanmatchpool (Integer gametypekey) throws Opexception {Treemap<integer, queue< ;iunit>> Matchpool = This.matchPools.get (Gametype.get (Gametypekey)); if (Matchpool = = null) {throw new Opexception (Opresult.invalid, "the tournament type is not found" + gametypekey); } matchpool.clear (); } @Override public void Afterpropertiesset () throws Exception {for (GameType GameType:GameType.getall (). VA Lues ()) {Matchpools.put (gametype, New Treemap<integer, queue<iunit>> ()); } }}
Data
Iunit
Package com.fym.match.obj;/** * * Created by fengy on 2016/5/25. */public interface Iunit { int getscore ();
Person
Package com.fym.match.obj;/** * * Created by fengy on 2016/5/25. */public class Person implements Iunit { /** * Player ID */ private int pid; /** * Score * */ private int score; public person (int pid, int score) { this.pid = pid; This.score = score; } @Override public int Getscore () { return this.score; } @Override public int GetSize () { return 1;
Group
Package Com.fym.match.obj;import java.util.arraylist;import Java.util.collection;import java.util.List;/** * * Created by Fengy on 2016/5/25. */public class Group implements Iunit { /** * score */ private int score; /** * Player ID */ private list<person> persons; Public Group (collection<person> persons) { this.persons = new arraylist<> (persons); int sum = 0; for (person person:this.persons) { sum + = Person.getscore (); } This.score = sum/persons.size () + persons.size () *; } @Override public int Getscore () { return this.score; } @Override public int GetSize () { return this.persons.size ();
Match
Package Com.fym.match.obj;import Com.fym.game.enm.gametype;import Java.util.arraylist;import java.util.List;/** * * Created by Fengy on 2016/5/25. */public class Match {public gametype gametype; Public list<iunit> team1; Public list<iunit> team2; Public Match (GameType gametype) {this.gametype = GameType; this.team1 = new arraylist<> (gametype.pcnt); THIS.TEAM2 = new arraylist<> (gametype.pcnt); } public int team1size () {return this.teamsizecore (THIS.TEAM1); } public int team2size () {return this.teamsizecore (THIS.TEAM2); } private static int teamsizecore (list<iunit> team) {int size = 0; for (Iunit iunit:team) {size = size + iunit.getsize (); } return size; } public int Team1score () {return this.teamscorecore (THIS.TEAM1); } public int Team2score () {return this.teamscorecore (THIS.TEAM2); } public int Scorediff () { Return (This.team1score ()-This.team2score ()); } private static int teamscorecore (list<iunit> team) {int score = 0; for (Iunit iunit:team) {score = score + iunit.getsize (); } return score; }}
Ladder matching System-simple implementation