package cryodex.modules.xwing; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JOptionPane; import cryodex.CryodexController; import cryodex.CryodexController.Modules; import cryodex.Main; import cryodex.Player; import cryodex.modules.Module; import cryodex.modules.Tournament; import cryodex.xml.XMLObject; import cryodex.xml.XMLUtils; import cryodex.xml.XMLUtils.Element; public class XWingTournament implements XMLObject, Tournament { public enum InitialSeedingEnum { RANDOM, BY_GROUP, IN_ORDER; } private final List<XWingRound> rounds; private List<XWingPlayer> players; private InitialSeedingEnum seedingEnum; private final XWingTournamentGUI tournamentGUI; private String name; private List<Integer> points; private boolean startAsSingleElimination = false; private boolean startAsRoundRobin = false; private List<String> dependentTournaments; public XWingTournament(Element tournamentElement) { this.players = new ArrayList<>(); this.rounds = new ArrayList<>(); this.dependentTournaments = new ArrayList<>(); seedingEnum = InitialSeedingEnum.RANDOM; tournamentGUI = new XWingTournamentGUI(this); String playerIDs = tournamentElement.getStringFromChild("PLAYERS"); Module m = Modules.getModuleByName(getModuleName()); for (String s : playerIDs.split(",")) { Player p = CryodexController.getPlayerByID(s); if (p != null) { XWingPlayer xp = (XWingPlayer) p.getModuleInfoByModule(m); if (xp != null) { players.add(xp); } } } Element roundElement = tournamentElement.getChild("ROUNDS"); for (Element e : roundElement.getChildren()) { rounds.add(new XWingRound(e, this)); } Element dependentElement = tournamentElement.getChild("DEPENDENTTOURNAMENTS"); if (dependentElement != null) { for (Element e : dependentElement.getChildren()) { dependentTournaments.add(e.getData()); } } name = tournamentElement.getStringFromChild("NAME"); String seeding = tournamentElement.getStringFromChild("SEEDING"); if (seeding != null && seeding.isEmpty() == false) { seedingEnum = InitialSeedingEnum.valueOf(seeding); } else { seedingEnum = InitialSeedingEnum.RANDOM; } String pointsString = tournamentElement.getStringFromChild("POINTS"); if (pointsString != null && pointsString.isEmpty() == false) { points = new ArrayList<Integer>(); for (String s : pointsString.split(",")) { points.add(new Integer(s)); } } int counter = 1; for (XWingRound r : rounds) { if (r.isSingleElimination()) { getTournamentGUI().getRoundTabbedPane().addSingleEliminationTab(r.getMatches().size() * 2, r.getPanel()); } else { if (r.isRoundRobin()) { this.startAsRoundRobin = true; } getTournamentGUI().getRoundTabbedPane().addSwissTab(counter, r.getPanel()); counter++; } } getTournamentGUI().getRankingTable().setPlayers(getAllXWingPlayers()); } public XWingTournament(String name, List<XWingPlayer> players, InitialSeedingEnum seedingEnum, List<Integer> points, boolean isSingleElimination, boolean isRoundRobin) { this.name = name; this.players = new ArrayList<>(players); this.rounds = new ArrayList<>(); this.seedingEnum = seedingEnum; this.points = points; this.startAsSingleElimination = isSingleElimination; this.startAsRoundRobin = isRoundRobin; this.dependentTournaments = new ArrayList<String>(); tournamentGUI = new XWingTournamentGUI(this); } public XWingRound getLatestRound() { if (rounds == null || rounds.isEmpty()) { return null; } else { return rounds.get(rounds.size() - 1); } } public int getRoundNumber(XWingRound round) { int count = 0; for (XWingRound r : rounds) { count++; if (r == round) { return count; } } return 0; } public XWingRound getRound(int i) { if (rounds == null) { return null; } else { return rounds.get(i); } } public XWingRound getSelectedRound() { if (rounds == null) { return null; } else { return getAllRounds().get(getTournamentGUI().getRoundTabbedPane().getSelectedIndex()); } } public List<XWingRound> getAllRounds() { return rounds; } @Override public int getRoundCount() { if (rounds == null) { return 0; } else { return rounds.size(); } } @Override public void setPlayers(List<Player> players) { List<XWingPlayer> xwPlayers = new ArrayList<>(); for (Player p : players) { XWingPlayer xp = new XWingPlayer(p); xwPlayers.add(xp); } setXWingPlayer(xwPlayers); } @Override public List<Player> getPlayers() { List<Player> players = new ArrayList<Player>(); for (XWingPlayer xp : getXWingPlayers()) { players.add(xp.getPlayer()); } return players; } public List<XWingPlayer> getXWingPlayers() { return players; } public void setXWingPlayer(List<XWingPlayer> players) { this.players = players; } /** * Returns any players have have played at least one match. This calls back dropped players into the list. * * @return */ public Set<XWingPlayer> getAllXWingPlayers() { // TreeSets and Head To Head comparisons can have problems. // Do not use them together. Set<XWingPlayer> allPlayers = new TreeSet<XWingPlayer>(new XWingComparator(this, XWingComparator.rankingCompareNoHeadToHead)); for (XWingRound r : getAllRounds()) { for (XWingMatch m : r.getMatches()) { if (m.isBye()) { allPlayers.add(m.getPlayer1()); } else { allPlayers.add(m.getPlayer1()); if (m.getPlayer2() != null) { allPlayers.add(m.getPlayer2()); } } } } allPlayers.addAll(players); return allPlayers; } @Override public Set<Player> getAllPlayers() { Set<Player> players = new TreeSet<Player>(); for (XWingPlayer xp : getAllXWingPlayers()) { players.add(xp.getPlayer()); } return players; } @Override public XWingTournamentGUI getTournamentGUI() { return tournamentGUI; } @Override public String getName() { return name; } public List<Integer> getPoints() { return points; } @Override public void setName(String name) { this.name = name; } @Override public void updateVisualOptions() { if (CryodexController.isLoading == false) { for (XWingRound r : getAllRounds()) { r.getPanel().resetGamePanels(true); } } } @Override public boolean generateNextRound() { // All matches must have a result filled in if (getLatestRound().isComplete() == false) { JOptionPane.showMessageDialog(Main.getInstance(), "Current round is not complete. Please complete all matches before continuing"); return false; } // Single elimination checks if (getLatestRound().isSingleElimination()) { // If there was only one match then there is no reason to create a // new round. if (getLatestRound().getMatches().size() == 1) { JOptionPane.showMessageDialog(Main.getInstance(), "Final tournament complete. No more rounds will be generated."); return false; } if (getLatestRound().isValid() == false) { JOptionPane.showMessageDialog(Main.getInstance(), "At least one tournamnt result is not correct.\n" + "-Check if points are backwards or a draw has been set.\n" + "-Draws are not allowed in single elimination rounds.\n" + "--If a draw occurs, the player with initiative wins.\n" + "--This can be set by going to the X-Wing menu then the view submenu and deselect 'Enter Only Points'"); return false; } generateSingleEliminationMatches(getLatestRound().getMatches().size()); } else { // Regular swiss round checks if (getLatestRound().isValid() == false) { JOptionPane.showMessageDialog(Main.getInstance(), "At least one tournamnt result is not correct. Check if points are backwards or a result should be a modified win or tie."); return false; } if (startAsRoundRobin) { if ((players.size() % 2 == 1 && rounds.size() >= players.size()) || (players.size() % 2 == 0 && rounds.size() >= players.size() - 1)) { JOptionPane.showMessageDialog(Main.getInstance(), "Final tournament complete. No more rounds will be generated."); return false; } } generateRound(getAllRounds().size() + 1); } return true; } @Override public void cancelRound(int roundNumber) { if (rounds.size() >= roundNumber) { // If we are generating a past round. Clear all existing rounds that // will be erased. while (rounds.size() >= roundNumber) { int index = rounds.size() - 1; XWingRound roundToRemove = rounds.get(index); for (XWingMatch m : roundToRemove.getMatches()) { m.setWinner(null); m.setBye(false); m.setPlayer1(null); m.setPlayer2(null); m.setPlayer1PointsDestroyed(null); m.setPlayer2PointsDestroyed(null); } rounds.remove(roundToRemove); getTournamentGUI().getRoundTabbedPane().remove(index); } } } private void generateRoundRobinRound(int roundNumber) { List<XWingMatch> matches; matches = new ArrayList<XWingMatch>(); List<XWingPlayer> tempList = new ArrayList<>(); tempList.addAll(getXWingPlayers()); // "Rotate" some or all of the players to // make the matchups XWingPlayer tempPlayer; // if the number of players are odd, rotate the whole // list of players // if the number of players are even, rotate all // but one player int rotationPlace = 0; if (tempList.size() % 2 == 0) { rotationPlace++; } // once for each round after the first, // move one player to the back of the list // Either the first in the list (for an odd nr of players) // Or the second in the list (for an even nr of players) for (int n = 1; n < roundNumber; n++) { tempPlayer = tempList.remove(rotationPlace); tempList.add(tempPlayer); } while (tempList.isEmpty() == false) { XWingPlayer player1 = tempList.get(0); XWingPlayer player2 = null; tempList.remove(0); if (tempList.isEmpty() == false) { player2 = tempList.get(0); tempList.remove(0); } XWingMatch match = new XWingMatch(player1, player2); matches.add(match); } XWingRound r = new XWingRound(matches, this, roundNumber); r.setRoundRobin(true); rounds.add(r); { getTournamentGUI().getRoundTabbedPane().addSwissTab(roundNumber, r.getPanel()); } getTournamentGUI().getRankingTable().setPlayers(getAllXWingPlayers()); } @Override public void generateRound(int roundNumber) { // if trying to skip a round...stop it if (roundNumber > rounds.size() + 1) { throw new IllegalArgumentException(); } cancelRound(roundNumber); if (startAsRoundRobin) { generateRoundRobinRound(roundNumber); return; } List<XWingMatch> matches; boolean hasDependentTournaments = dependentTournaments != null && dependentTournaments.isEmpty() == false; if (roundNumber == 1 && hasDependentTournaments == false) { matches = new ArrayList<XWingMatch>(); List<XWingPlayer> tempList = new ArrayList<>(); tempList.addAll(getXWingPlayers()); List<XWingPlayer> firstRoundByePlayers = new ArrayList<>(); for (XWingPlayer p : tempList) { if (p.isFirstRoundBye()) { firstRoundByePlayers.add(p); } } tempList.removeAll(firstRoundByePlayers); if (seedingEnum == InitialSeedingEnum.IN_ORDER) { while (tempList.isEmpty() == false) { XWingPlayer player1 = tempList.get(0); XWingPlayer player2 = null; tempList.remove(0); if (tempList.isEmpty() == false) { player2 = tempList.get(0); tempList.remove(0); } XWingMatch match = new XWingMatch(player1, player2); matches.add(match); } } else if (seedingEnum == InitialSeedingEnum.RANDOM) { Collections.shuffle(tempList); while (tempList.isEmpty() == false) { XWingPlayer player1 = tempList.get(0); XWingPlayer player2 = tempList.get(tempList.size() - 1); tempList.remove(player1); if (player1 == player2) { player2 = null; } else { tempList.remove(player2); } XWingMatch match = new XWingMatch(player1, player2); matches.add(match); } } else if (seedingEnum == InitialSeedingEnum.BY_GROUP) { Map<String, List<XWingPlayer>> playerMap = new HashMap<String, List<XWingPlayer>>(); // Add players to map for (XWingPlayer p : tempList) { List<XWingPlayer> playerList = playerMap.get(p.getPlayer().getGroupName()); if (playerList == null) { playerList = new ArrayList<>(); String groupName = p.getPlayer().getGroupName() == null ? "" : p.getPlayer().getGroupName(); playerMap.put(groupName, playerList); } playerList.add(p); } // Shuffle up the lists List<String> seedValues = new ArrayList<>(playerMap.keySet()); Collections.shuffle(seedValues); // Shuffle each group list for (List<XWingPlayer> list : playerMap.values()) { Collections.shuffle(list); } XWingPlayer p1 = null; XWingPlayer p2 = null; while (seedValues.isEmpty() == false) { int i = 0; String lastSeedValue = null; while (i < seedValues.size()) { lastSeedValue = seedValues.get(i); if (p1 == null) { p1 = playerMap.get(lastSeedValue).get(0); } else { p2 = playerMap.get(lastSeedValue).get(0); matches.add(new XWingMatch(p1, p2)); p1 = null; p2 = null; } playerMap.get(lastSeedValue).remove(0); if (playerMap.get(lastSeedValue).isEmpty()) { seedValues.remove(i); } else { i++; } } Collections.shuffle(seedValues); while (seedValues.size() > 1 && seedValues.get(0) == lastSeedValue) { Collections.shuffle(seedValues); } } if (p1 != null) { matches.add(new XWingMatch(p1, null)); } } for (XWingPlayer p : firstRoundByePlayers) { matches.add(new XWingMatch(p, null)); } } else { matches = getMatches(getXWingPlayers()); } XWingRound r = new XWingRound(matches, this, roundNumber); rounds.add(r); if (roundNumber == 1 && startAsSingleElimination && (matches.size() == 1 || matches.size() == 2 || matches.size() == 4 || matches.size() == 8 || matches.size() == 16 || matches.size() == 32)) { r.setSingleElimination(true); getTournamentGUI().getRoundTabbedPane().addSingleEliminationTab(r.getMatches().size() * 2, r.getPanel()); } else { getTournamentGUI().getRoundTabbedPane().addSwissTab(roundNumber, r.getPanel()); } getTournamentGUI().getRankingTable().setPlayers(getAllXWingPlayers()); } private List<XWingMatch> getMatches(List<XWingPlayer> userList) { List<XWingMatch> matches = new ArrayList<XWingMatch>(); List<XWingPlayer> tempList = new ArrayList<XWingPlayer>(); tempList.addAll(userList); Collections.sort(tempList, new XWingComparator(this, XWingComparator.pairingCompare)); XWingMatch byeMatch = null; // Setup the bye match if necessary // The player to get the bye is the lowest ranked player who has not had // a bye yet or who has the fewest byes if (tempList.size() % 2 == 1) { XWingPlayer byeUser = null; int byUserCounter = 1; int minByes = 0; try { while (byeUser == null || byeUser.getByes(this) > minByes || (byeUser.getMatches(this) != null && byeUser.getMatches(this).get(byeUser.getMatches(this).size() - 1).isBye())) { if (byUserCounter > tempList.size()) { minByes++; byUserCounter = 1; } byeUser = tempList.get(tempList.size() - byUserCounter); byUserCounter++; } } catch (ArrayIndexOutOfBoundsException e) { byeUser = tempList.get(tempList.size() - 1); } byeMatch = new XWingMatch(byeUser, null); tempList.remove(byeUser); } matches = new XWingRandomMatchGeneration(this, tempList).generateMatches(); if (XWingMatch.hasDuplicate(matches)) { JOptionPane.showMessageDialog(Main.getInstance(), "Unable to resolve duplicate matches. Please review for best course of action."); } // Add the bye match at the end if (byeMatch != null) { matches.add(byeMatch); } return matches; } @Override public void generateSingleEliminationMatches(int cutSize) { List<XWingMatch> matches = new ArrayList<>(); List<XWingMatch> matchesCorrected = new ArrayList<XWingMatch>(); if (getLatestRound().isSingleElimination()) { List<XWingMatch> lastRoundMatches = getLatestRound().getMatches(); for (int index = 0; index < lastRoundMatches.size(); index = index + 2) { XWingMatch newMatch = new XWingMatch(lastRoundMatches.get(index).getWinner(), lastRoundMatches.get(index + 1).getWinner()); matches.add(newMatch); } matchesCorrected = matches; } else { List<XWingPlayer> tempList = new ArrayList<>(); tempList.addAll(getXWingPlayers()); Collections.sort(tempList, new XWingComparator(this, XWingComparator.rankingCompare)); tempList = tempList.subList(0, cutSize); while (tempList.isEmpty() == false) { XWingPlayer player1 = tempList.get(0); XWingPlayer player2 = tempList.get(tempList.size() - 1); tempList.remove(player1); if (player1 == player2) { player2 = null; } else { tempList.remove(player2); } XWingMatch match = new XWingMatch(player1, player2); matches.add(match); } switch (matches.size()) { case 4: matchesCorrected.add(matches.get(0)); matchesCorrected.add(matches.get(3)); matchesCorrected.add(matches.get(2)); matchesCorrected.add(matches.get(1)); break; case 8: matchesCorrected.add(matches.get(0)); matchesCorrected.add(matches.get(7)); matchesCorrected.add(matches.get(4)); matchesCorrected.add(matches.get(3)); matchesCorrected.add(matches.get(2)); matchesCorrected.add(matches.get(5)); matchesCorrected.add(matches.get(6)); matchesCorrected.add(matches.get(1)); break; default: matchesCorrected = matches; } } XWingRound r = new XWingRound(matchesCorrected, this, null); r.setSingleElimination(true); rounds.add(r); getTournamentGUI().getRoundTabbedPane().addSingleEliminationTab(cutSize, r.getPanel()); CryodexController.saveData(); } @Override public StringBuilder appendXML(StringBuilder sb) { String playerString = ""; String seperator = ""; for (XWingPlayer p : players) { playerString += seperator + p.getPlayer().getSaveId(); seperator = ","; } XMLUtils.appendObject(sb, "PLAYERS", playerString); XMLUtils.appendList(sb, "ROUNDS", "ROUND", getAllRounds()); String pointsString = ""; seperator = ""; if (points != null) { for (Integer p : points) { pointsString += seperator + p; seperator = ","; } } XMLUtils.appendObject(sb, "POINTS", pointsString); XMLUtils.appendObject(sb, "NAME", name); XMLUtils.appendObject(sb, "MODULE", Modules.XWING.getName()); XMLUtils.appendObject(sb, "SEEDING", seedingEnum); XMLUtils.appendStringList(sb, "DEPENDENTTOURNAMENTS", "DEPENDENT", dependentTournaments); return sb; } @Override public void startTournament() { generateRound(1); } @Override public void addPlayer(Player p) { for (XWingRound r : getAllRounds()) { for (XWingMatch m : r.getMatches()) { if (m.getPlayer1().getPlayer().equals(p)) { getXWingPlayers().add(m.getPlayer1()); return; } else if (m.getPlayer2() != null && m.getPlayer2().getPlayer().equals(p)) { getXWingPlayers().add(m.getPlayer2()); return; } } } XWingPlayer xPlayer = new XWingPlayer(p); getXWingPlayers().add(xPlayer); } @Override public void dropPlayer(Player p) { XWingPlayer xPlayer = null; for (XWingPlayer xp : getXWingPlayers()) { if (xp.getPlayer() == p) { xPlayer = xp; break; } } if (xPlayer != null) { getXWingPlayers().remove(xPlayer); } resetRankingTable(); } @Override public void massDropPlayers(int minScore, int maxCount) { List<XWingPlayer> playerList = new ArrayList<XWingPlayer>(); playerList.addAll(getXWingPlayers()); Collections.sort(playerList, new XWingComparator(this, XWingComparator.rankingCompare)); int count = 0; for (XWingPlayer xp : playerList) { if (xp.getScore(this) < minScore || count >= maxCount) { getXWingPlayers().remove(xp); } else { count++; } } resetRankingTable(); } @Override public void massDropPlayers(List<Player> playersToDrop) { for (Player p : playersToDrop) { dropPlayer(p); } resetRankingTable(); } @Override public void resetRankingTable() { getTournamentGUI().getRankingTable().setPlayers(getAllXWingPlayers()); } @Override public Icon getIcon() { URL imgURL = XWingTournament.class.getResource("x.png"); if (imgURL == null) { System.out.println("Failed to retrieve X-Wing Icon"); } ImageIcon icon = new ImageIcon(imgURL); return icon; } @Override public String getModuleName() { return Modules.XWING.getName(); } public int getRoundPoints(int roundNumber) { int tournamentPoints = 100; if (getPoints() != null && getPoints().isEmpty() == false) { tournamentPoints = getPoints().size() >= roundNumber ? getPoints().get(roundNumber - 1) : getPoints().get(getPoints().size() - 1); } return tournamentPoints; } public void addDependentTournaments(List<XWingTournament> tournaments) { for (XWingTournament t : tournaments) { dependentTournaments.add(t.getName()); } } public List<XWingTournament> getDependentTournaments() { if (dependentTournaments == null || dependentTournaments.isEmpty()) { return new ArrayList<XWingTournament>(); } List<XWingTournament> dependentList = new ArrayList<XWingTournament>(); for (Tournament t : CryodexController.getAllTournaments()) { if (t instanceof XWingTournament && dependentTournaments.contains(t.getName())) { dependentList.add((XWingTournament) t); } } return dependentList; } public void triggerChange() { getTournamentGUI().getRankingTable().resetPlayers(); CryodexController.saveData(); } }