package cryodex.modules.destiny; import java.util.ArrayList; import java.util.Collections; import java.util.List; import javax.swing.JOptionPane; import cryodex.Main; /** * Generate matches in order of ranking or as close to it as possible. This * algorithm finds the best possible match set that holds truest to the original * ranking. Every permutation is calculated and given a score based on the * distance each player is from their true ranking. The permutation with the * lowest score wins. * * @author cbrown * */ public class DestinyOrderedMatchGeneration { private final DestinyTournament tournament; private final List<DestinyPlayer> players; private Integer lowScore = null; private List<DestinyMatch> matchSetAtLowScore = null; public DestinyOrderedMatchGeneration(DestinyTournament tournament, List<DestinyPlayer> players) { this.tournament = tournament; this.players = players; } public List<DestinyMatch> generateMatches() { List<DestinyPlayer> tempList = new ArrayList<>(); tempList.addAll(players); Collections.sort(tempList, new DestinyComparator(tournament, DestinyComparator.pairingCompare)); generateMatch(new ArrayList<DestinyMatch>(), tempList); // If no valid match set was found then we create the true ranking match // set and return it if (matchSetAtLowScore == null) { matchSetAtLowScore = new ArrayList<>(); for (int counter = 0; counter < players.size(); counter += 2) { DestinyMatch m = new DestinyMatch(players.get(counter), players.get(counter + 1)); m.checkDuplicate(tournament.getAllRounds()); matchSetAtLowScore.add(m); } } if (lowScore != null && lowScore != 0) { JOptionPane.showMessageDialog(Main.getInstance(), "Matches were modified to avoid duplicate pairings. Avoidance score = " + lowScore + " (2-10 is a minor change)"); } return matchSetAtLowScore; } private void generateMatch(List<DestinyMatch> matches, List<DestinyPlayer> player1List) { if (player1List.size() == 0) { scorePermutation(matches); return; } for (DestinyPlayer xp : player1List) { List<DestinyPlayer> player2List = new ArrayList<>(); player2List.addAll(player1List); player2List.remove(xp); getPlayer2(xp, matches, player2List); if (lowScore != null && lowScore <= 2) { return; } } } private void getPlayer2(DestinyPlayer player1, List<DestinyMatch> matches, List<DestinyPlayer> player2List) { for (DestinyPlayer player2 : player2List) { DestinyMatch xm = new DestinyMatch(player1, player2); xm.checkDuplicate(tournament.getAllRounds()); if (xm.isDuplicate() == false) { matches.add(xm); if (shouldContinue(matches)) { List<DestinyPlayer> player1List = new ArrayList<>(); player1List.addAll(player2List); player1List.remove(xm.getPlayer2()); generateMatch(matches, player1List); } } matches.remove(xm); if (lowScore != null && lowScore <= 2) { return; } } } private boolean shouldContinue(List<DestinyMatch> matches) { if (lowScore == null) { return true; } return getScore(matches) < lowScore; } private void scorePermutation(List<DestinyMatch> matches) { int score = getScore(matches); if (lowScore == null || score < lowScore) { matchSetAtLowScore = new ArrayList<>(); matchSetAtLowScore.addAll(matches); lowScore = score; } } private int getScore(List<DestinyMatch> matches) { // order players Collections.sort(players, new DestinyComparator(tournament, DestinyComparator.pairingCompare)); // get list of players in order of matches List<DestinyPlayer> playerByMatchOrder = new ArrayList<DestinyPlayer>(); for (DestinyMatch xm : matches) { playerByMatchOrder.add(xm.getPlayer1()); playerByMatchOrder.add(xm.getPlayer2()); } int score = 0; for (int counter = 0; counter < playerByMatchOrder.size(); counter++) { int index = players.indexOf(playerByMatchOrder.get(counter)); score += Math.abs(counter - index); } return score; } }