package com.e2u.sort;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
public class MatchSort implements PermCallBack
{
private int curRound = MatchUtil.COMMON_INVALID;
private int playerCount = MatchUtil.COMMON_INVALID;
private int roundCount = MatchUtil.COMMON_INVALID;
private int minorScoreMode = MatchUtil.MINOR_SCORE_OPPONENT_MODE;
private int matchIDGenerator = 0;
private TreeMap<Integer, SectionSelectee> scoreSectionMap = new TreeMap<Integer, SectionSelectee>(ComparatorFactory.getComparator4Score());
private Set<Integer> matchedPlayerSet = new HashSet<Integer>();
private Map<Integer, Integer> failedMap = new HashMap<Integer, Integer>();
private List<Player> failedList = new ArrayList<Player>();
private List<Match> preProcessMatchList = new ArrayList<Match>();
private TreeSet<Integer> curRoundMatchIDSet = new TreeSet<Integer>();
private int failedTime = 0;
public MatchSort(int playerCount, int roundCount)
{
this.playerCount = playerCount;
if(roundCount <= 0)
{
this.roundCount = MatchUtil.log2(this.playerCount) + 2;
}
else
{
this.roundCount = roundCount;
}
this.curRound = 0;
}
public int getCurRound()
{
return curRound;
}
public int getPlayerCount()
{
return playerCount;
}
public int getRoundCount()
{
return roundCount;
}
private void updateRound()
{
curRound++;
generateCurRoundMatchID();
}
private synchronized int generateMatchID()
{
matchIDGenerator++;
return matchIDGenerator;
}
private synchronized int selectFirstMatchID()
{
if(curRoundMatchIDSet.isEmpty())
{
throw new IllegalArgumentException("[Fatal Error]: No available Match ID");
}
int firstID = curRoundMatchIDSet.first();
curRoundMatchIDSet.remove(firstID);
return firstID;
}
private synchronized int getFirstMatchID()
{
if(curRoundMatchIDSet.isEmpty())
{
throw new IllegalArgumentException("[Fatal Error]: No available Match ID");
}
return curRoundMatchIDSet.first();
}
private synchronized void removeMatch(Map<Integer, Match> map, int matchID)
{
if(!map.containsKey(matchID))
{
throw new IllegalArgumentException("[Fatal Error]: The match ID doesn't exist, matchID = " + matchID);
}
Match match = map.get(matchID);
map.remove(matchID);
matchedPlayerSet.remove(match.player1.playerID);
matchedPlayerSet.remove(match.player2.playerID);
curRoundMatchIDSet.add(matchID);
}
public void initPlayerInformation(int count)
{
for(int i = 0; i < count; i++)
{
Player player = new Player();
player.id = (i + 1);
player.name = "player" + player.id;
player.score = 0;
player.matchList = new ArrayList<Integer>();
MatchDataSource.getInstance().getPlayerMap().put(Integer.valueOf(player.id), player);
}
}
public void startArrange()
{
updateRound();
int matchCount = MatchDataSource.getInstance().getPlayerMap().size() / 2;
Map<Integer, Match> map = new TreeMap<Integer, Match>();
for(int i = 0; i < matchCount; i++)
{
Match match = generateMatch( (i + 1), matchCount + (i + 1), (i % 2) == 0 );
map.put(Integer.valueOf(match.id), match);
}
MatchDataSource.getInstance().getMatchMap().put(Integer.valueOf(this.curRound), map);
}
public void playMath()
{
Map<Integer, Match> map = MatchDataSource.getInstance().getMatchMap().get(Integer.valueOf(this.curRound));
Player player = null;
for(Iterator iter = map.entrySet().iterator(); iter.hasNext(); )
{
Map.Entry<Integer, Match> entry = (Map.Entry<Integer, Match>)iter.next();
Match match = entry.getValue();
match.result = (byte)MatchUtil.randPlayMatch();
match.player1.foul = MatchUtil.randFoul();
match.player2.foul = MatchUtil.randFoul();
player = MatchDataSource.getInstance().getPlayer(match.player1.playerID);
player.addMatch(match.id);
player = MatchDataSource.getInstance().getPlayer(match.player2.playerID);
player.addMatch(match.id);
}
}
public void showResult()
{
Map<Integer, Match> map = MatchDataSource.getInstance().getMatchMap().get(Integer.valueOf(this.curRound));
for(Iterator iter = map.entrySet().iterator(); iter.hasNext(); )
{
Map.Entry<Integer, Match> entry = (Map.Entry<Integer, Match>)iter.next();
Match match = entry.getValue();
showMatch(match);
}
}
public void arrange()
{
updateRound();
updateAllPlayers();
sortCurrentPlayers();
matchedPlayerSet.clear();
failedMap.clear();
failedList.clear();
preProcessMatchList.clear();
TreeMap<Integer, Match> map = new TreeMap<Integer, Match>();
doArrangeMatch(map);
MatchDataSource.getInstance().getMatchMap().put(Integer.valueOf(this.curRound), map);
}
private void generateCurRoundMatchID()
{
curRoundMatchIDSet.clear();
int half = playerCount / 2;
for(int i = 0; i < half; i++)
{
curRoundMatchIDSet.add(generateMatchID());
}
}
private Player getMatchedPlayer(Player player)
{
Iterator<Map.Entry<Integer, SectionSelectee>> iter = null;
SectionSelectee ssee = null;
Player candidatePlayer = null;
for(iter = scoreSectionMap.entrySet().iterator(); iter.hasNext(); )
{
Map.Entry<Integer, SectionSelectee> entry = iter.next();
ssee = entry.getValue();
//First
if(player.requireFirstThisRound() > 0)
{
candidatePlayer = getMatchedPlayer(player, ssee.reqSecondPlayerList, false);
if(candidatePlayer != null)
{
return candidatePlayer;
}
candidatePlayer = getMatchedPlayer(player, ssee.reqFirstPlayerList, true);
if(candidatePlayer != null)
{
return candidatePlayer;
}
}
else
{
candidatePlayer = getMatchedPlayer(player, ssee.reqFirstPlayerList, false);
if(candidatePlayer != null)
{
return candidatePlayer;
}
candidatePlayer = getMatchedPlayer(player, ssee.reqSecondPlayerList, true);
if(candidatePlayer != null)
{
return candidatePlayer;
}
}
}
return null;
}
private boolean arrangeMatch(List<Player> playerList, Map<Integer, Match> map)
{
if(playerList == null || playerList.isEmpty())
{
return true;
}
Player p1 = null, p2 = null;
Iterator<Player> p1Iter = null;
for(p1Iter = playerList.iterator(); p1Iter.hasNext(); )
{
p1 = p1Iter.next();
if(matchedPlayerSet.contains(p1.id))
{
continue;
}
p2 = getMatchedPlayer(p1);
if(p2 == null)
{
MatchUtil.debug(String.format("[START]: p1=%2d, NO OPPONENT", p1.id));
incFailedTime(p1.id);
MatchUtil.debug(String.format("[END]: p1=%2d, NO OPPONENT", p1.id));
continue;
/*
MatchUtil.debug(String.format("[START]: p1=%2d, NO OPPONENT", p1.id));
List<Player> missMatchPlayerList = new ArrayList<Player>();
incFailedTime(p1.id);
if(getFailedTime(p1.id) >= 2)
{
missMatchPlayerList.addAll(failedList);
}
else
{
missMatchPlayerList.add(p1);
// p2 = getNextPlayer(p1);
// if(p2 != null)
// {
// missMatchPlayerList.add(p2);
// }
}
if(!searchMatchForFailed(missMatchPlayerList, map))
{
throw new IllegalArgumentException("DeadLock, missMatchPlayerList = " + missMatchPlayerList);
}
missMatchPlayerList.clear();
Match match = null;
for(int i = 0, size = preProcessMatchList.size(); i < size; i++)
{
match = preProcessMatchList.get(i);
match = generateMatch(match.player1.playerID, match.player2.playerID);
map.put(match.id, match);
}
preProcessMatchList.clear();
MatchUtil.debug(String.format("[END]: p1=%2d, NO OPPONENT", p1.id));
return true;
*/
}
Match match = generateMatch(p1, p2);
map.put(match.id, match);
matchedPlayerSet.add(p1.id);
matchedPlayerSet.add(p2.id);
}
return true;
}
private Match searchOpponentInMatch(TreeMap<Integer, Match> map, Player player)
{
for(int key = map.lastKey(); !map.isEmpty(); key = map.lowerKey(key))
{
Match match = map.get(key);
if(!MatchDataSource.getInstance().isFightBefore(player.id, match.player1.playerID))
{
return match;
}
if(!MatchDataSource.getInstance().isFightBefore(player.id, match.player2.playerID))
{
return match;
}
}
return null;
}
public boolean searchMatchForFailed(List<Player> playerList, TreeMap<Integer, Match> map)
{
boolean ret = false;
//1. rotate outside
ret = rotateSearch(playerList, map, true);
if(ret)
{
return true;
}
//2. rotate inside
ret = rotateSearch(playerList, map, false);
if(ret)
{
return true;
}
List params = new ArrayList(2);
params.add(playerList);
params.add(map);
//3. perm outside
params.add(Boolean.TRUE);
ret = MatchUtil.perm(playerList.size(), this, params);
if(ret)
{
return true;
}
//4. perm inside
params.set(2, Boolean.FALSE);
ret = MatchUtil.perm(playerList.size(), this, params);
if(ret)
{
return true;
}
return false;
}
//Rotate use
public boolean rotateSearch(List<Player> playerList, TreeMap<Integer, Match> map, boolean isOutSide)
{
boolean ret = false;
for(int i = 0, size = playerList.size(); i < size; i++)
{
if(isOutSide)
{
ret = searchMatchOutFailedList(playerList, map, null);
}
else
{
ret = searchMatchInFailedList(playerList, map, null);
}
if(ret)
{
//restore orders
Collections.rotate(playerList, i);
return true;
}
//Move forward 1 step
Collections.rotate(playerList, -1);
}
return false;
}
//Perm use
public boolean doOper(int[] a, List params)
{
restorePreProcessMatchList();
List<Player> playerList = (List<Player>)params.get(0);
TreeMap<Integer, Match> map = (TreeMap<Integer, Match>)params.get(1);
Boolean bOutSide = (Boolean)params.get(2);
if(bOutSide.booleanValue())
{
return searchMatchOutFailedList(playerList, map, a);
}
else
{
return searchMatchInFailedList(playerList, map, a);
}
}
private boolean searchMatchOutFailedList(List<Player> playerList, TreeMap<Integer, Match> map, int a[])
{
restorePreProcessMatchList();
Player player = null, opponet = null;
for(int i = 0, size = playerList.size(); i < size; i++)
{
if(a != null)
{
player = playerList.get(a[i]);
}
else
{
player = playerList.get(i);
}
if(matchedPlayerSet.contains(player.id))
{
continue;
}
opponet = getMatchedPlayer(player);
while(opponet == null)
{
if(map.size() <= 0)
{
return false;
}
removePreviousMatches(Math.min(1, map.size()), map);
opponet = getMatchedPlayer(player);
}
preProcessMatchList.add(preGenerateMatch(player, opponet));
matchedPlayerSet.add(player.id);
matchedPlayerSet.add(opponet.id);
}
return true;
}
//Internal search
private boolean searchMatchInFailedList(List<Player> playerList, Map<Integer, Match> map, int a[])
{
restorePreProcessMatchList();
Player player = null, opponent = null;
int i, size = playerList.size();
for(i = 0; i < size - 1; i++)
{
if(a != null)
{
player = playerList.get(a[i]);
}
else
{
player = playerList.get(i);
}
if(matchedPlayerSet.contains(player.id))
{
continue;
}
opponent = null;
for(int j = i + 1; j < size; j++)
{
if(a != null)
{
opponent = playerList.get(a[j]);
}
else
{
opponent = playerList.get(j);
}
if(matchedPlayerSet.contains(opponent.id))
{
opponent = null;
continue;
}
if(MatchDataSource.getInstance().isFightBefore(player.id, opponent.id))
{
opponent = null;
continue;
}
else
{
break;
}
}
if(opponent == null)
{
return false;
}
else
{
preProcessMatchList.add(preGenerateMatch(player, opponent));
matchedPlayerSet.add(player.id);
matchedPlayerSet.add(opponent.id);
}
}
if( i == (size - 1) )
{
//Look for the opponent outside
opponent = getMatchedPlayer(player);
if(opponent == null)
{
return false;
}
else
{
preProcessMatchList.add(preGenerateMatch(player, opponent));
matchedPlayerSet.add(player.id);
matchedPlayerSet.add(opponent.id);
}
}
return true;
}
private void restorePreProcessMatchList()
{
Match match = null;
for(int i = 0, size = preProcessMatchList.size(); i < size; i++)
{
match = preProcessMatchList.get(i);
matchedPlayerSet.remove(match.player1.playerID);
matchedPlayerSet.remove(match.player2.playerID);
}
preProcessMatchList.clear();
}
private void incFailedTime(int playerID)
{
if(!failedMap.containsKey(playerID))
{
failedMap.put(playerID, 1);
failedList.add(MatchDataSource.getInstance().getPlayer(playerID));
}
else
{
int failed = failedMap.get(playerID);
failed++;
failedMap.put(playerID, failed);
}
}
private int getFailedTime(int playerID)
{
Integer failed = failedMap.get(playerID);
if(failed == null)
{
return 0;
}
return failed.intValue();
}
private int getGreaterFailedPlayer(int failed, List<Player> playerList)
{
int maxFailed = 0;
Player player = null;
for(Iterator<Map.Entry<Integer, Integer>> iter = failedMap.entrySet().iterator(); iter.hasNext(); )
{
Map.Entry<Integer, Integer> entry = iter.next();
if(entry.getValue() >= failed)
{
if(entry.getValue() > maxFailed)
{
maxFailed = entry.getValue();
}
player = MatchDataSource.getInstance().getPlayer(entry.getKey());
playerList.add(player);
player = getNextPlayer(player);
if(player != null)
{
playerList.add(player);
}
}
}
return maxFailed;
}
private Player getMatchedPlayer(Player player, List<Player> candidateList, boolean isReverse)
{
if(candidateList == null || candidateList.isEmpty())
{
return null;
}
Player candidatePlayer = null;
int i, end, step;
if(!isReverse)
{
i = 0;
end = candidateList.size();
step = 1;
}
else
{
i = candidateList.size() - 1;
end = -1;
step = -1;
}
for(; i != end; i += step)
{
candidatePlayer = candidateList.get(i);
//same one
if(candidatePlayer.id == player.id)
{
continue;
}
//has selected
if(matchedPlayerSet.contains(candidatePlayer.id))
{
continue;
}
//has fighted
if(MatchDataSource.getInstance().isFightBefore(player.id, candidatePlayer.id))
{
continue;
}
return candidatePlayer;
}
return null;
}
private void doArrangeMatch(TreeMap<Integer, Match> map)
{
Iterator<Map.Entry<Integer, SectionSelectee>> iter = null;
SectionSelectee ssee = null;
do
{
for(iter = scoreSectionMap.entrySet().iterator(); iter.hasNext(); )
{
Map.Entry<Integer, SectionSelectee> entry = iter.next();
ssee = entry.getValue();
if(!arrangeMatch(ssee.getLessPlayerList(), map))
{
break;
}
if(!arrangeMatch(ssee.getMorePlayerList(), map))
{
break;
}
}
if(!failedList.isEmpty())
{
if(!searchMatchForFailed(failedList, map))
{
throw new IllegalArgumentException("DeadLock, missMatchPlayerList = " + failedList);
}
failedList.clear();
failedMap.clear();
Match match = null;
for(int i = 0, size = preProcessMatchList.size(); i < size; i++)
{
match = preProcessMatchList.get(i);
match = generateMatch(match.player1.playerID, match.player2.playerID);
map.put(match.id, match);
}
preProcessMatchList.clear();
}
}
while(matchedPlayerSet.size() != MatchDataSource.getInstance().getPlayerMap().size());
}
private Match generateMatch(int player1ID, int player2ID)
{
Player p1 = MatchDataSource.getInstance().getPlayer(player1ID);
Player p2 = MatchDataSource.getInstance().getPlayer(player2ID);
return generateMatch(p1, p2);
}
private Match generateMatch(Player p1, Player p2)
{
Match match = new Match();
match.id = selectFirstMatchID();
Comparator<Player> comp = ComparatorFactory.getComparator4SelectFirst(curRound);
//p2 is less than p1, the less one is first.
if(comp.compare(p1, p2) > 0)
{
Player tempPlayer = p1;
p1 = p2;
p2 = tempPlayer;
}
match.player1 = new MatchPlayerInfo();
match.player1.playerID = p1.id;
match.player1.foul = 0;
match.player2 = new MatchPlayerInfo();
match.player2.playerID = p2.id;
match.player2.foul = 0;
return match;
}
private Match preGenerateMatch(Player p1, Player p2)
{
Match match = new Match();
match.player1 = new MatchPlayerInfo();
match.player1.playerID = p1.id;
match.player2 = new MatchPlayerInfo();
match.player2.playerID = p2.id;
return match;
}
private void removePreviousMatches(int previousCount, TreeMap<Integer, Match> map)
{
MatchUtil.debug("Start removePreviousMatches, previousCount=" + previousCount);
this.printSeparator();
Match match = null;
int matchID = 0;
for(int i = 0; i < previousCount; i++)
{
if(map.isEmpty())
{
throw new IllegalArgumentException("The match map is empty now");
}
matchID = map.lastKey();
match = map.get(matchID);
MatchUtil.debug("Removed: " + match.toString());
map.remove(matchID);
matchedPlayerSet.remove(match.player1.playerID);
matchedPlayerSet.remove(match.player2.playerID);
curRoundMatchIDSet.add(matchID);
}
// for(Iterator<Map.Entry<Integer, Match>> matchIter = map.entrySet().iterator(); matchIter.hasNext(); )
// {
// Map.Entry<Integer, Match> matchEntry = matchIter.next();
// showMatch(matchEntry.getValue());
// }
// this.printSeparator();
MatchUtil.debug("End removePreviousMatches, previousCount=" + previousCount);
}
private Match generateMatch(int p1, int p2, boolean isP1First)
{
Match match = new Match();
match.id = this.selectFirstMatchID();
if(!isP1First)
{
int tmp = p1;
p1 = p2;
p2 = tmp;
}
match.player1 = new MatchPlayerInfo();
match.player1.playerID = p1;
match.player1.foul = 0;
match.player2 = new MatchPlayerInfo();
match.player2.playerID = p2;
match.player2.foul = 0;
return match;
}
private void updateAllPlayers()
{
Collection<Player> players = MatchDataSource.getInstance().getPlayerMap().values();
for(Iterator<Player> iter = players.iterator(); iter.hasNext(); )
{
Player player = iter.next();
player.calAllOfflineMetrics();
}
}
public void sortCurrentPlayers()
{
scoreSectionMap.clear();
Collection<Player> players = MatchDataSource.getInstance().getPlayerMap().values();
SectionSelectee ssee = null;
for(Iterator<Player> iter = players.iterator(); iter.hasNext(); )
{
Player player = iter.next();
if(!scoreSectionMap.containsKey(player.score))
{
ssee = new SectionSelectee();
scoreSectionMap.put(player.score, ssee);
}
else
{
ssee = scoreSectionMap.get(player.score);
}
ssee.addPlayer(player);
}
Comparator<Player> comparator = ComparatorFactory.getComparatorInOneSection(curRound);
for(Iterator<SectionSelectee> iter = scoreSectionMap.values().iterator(); iter.hasNext(); )
{
ssee = iter.next();
ssee.sort(comparator);
}
if(MatchUtil.isDebug())
{
printSeparator();
for(Iterator<Map.Entry<Integer, SectionSelectee>> iter = scoreSectionMap.entrySet().iterator(); iter.hasNext(); )
{
Map.Entry<Integer, SectionSelectee> entry = iter.next();
showSectionSelectee(entry.getValue());
}
}
}
public void showArrangeTable()
{
Map<Integer, Match> map = MatchDataSource.getInstance().getMatchMap().get(Integer.valueOf(this.curRound));
for(Iterator iter = map.entrySet().iterator(); iter.hasNext(); )
{
Map.Entry<Integer, Match> entry = (Map.Entry<Integer, Match>)iter.next();
Match match = entry.getValue();
showMatch(match);
}
}
private Player getNextPlayer(Player player)
{
SectionSelectee ssee = scoreSectionMap.get(player.score);
if(ssee == null)
{
throw new IllegalArgumentException("[Fatal]: ssee = null, player = " + player);
}
Player next = ssee.getNextPlayer(player);
if(next == null)
{
Integer nextScore = scoreSectionMap.higherKey(player.score);
if(nextScore == null)
{
// throw new IllegalArgumentException("[Fatal]: nextScore == null, player = " + player);
MatchUtil.debug("[Fatal]: nextScore == null, player = " + player);
return null;
}
ssee = scoreSectionMap.get(nextScore);
next = ssee.getFirstPlayer();
}
return next;
}
public static void showMatch(Match match)
{
System.out.println(match);
}
public static void showPlayer(Player player)
{
System.out.println(player);
}
public static void showSectionSelectee(SectionSelectee ssee)
{
System.out.println(ssee);
}
public void printSeparator()
{
System.out.printf("================round %2d=================\n", curRound);
}
public static void main(String args[])
{
int playerCount = 10;
int roundCount = 9;
MatchSort matchSort = new MatchSort(playerCount, roundCount);
matchSort.initPlayerInformation(playerCount);
matchSort.startArrange();
for(int i = 0; i < roundCount; i++)
{
matchSort.printSeparator();
matchSort.showArrangeTable();
matchSort.playMath();
matchSort.printSeparator();
matchSort.showResult();
//not last round match
if(i < roundCount - 1)
{
matchSort.arrange();
}
}
matchSort.printSeparator();
matchSort.sortCurrentPlayers();
}
}