- Initial checkin for Tournaments with a (currently disabled) flag to make use of it in Quest Draft

This commit is contained in:
Sol
2016-03-25 20:06:52 +00:00
parent 952bb92d38
commit aca5777c4e
17 changed files with 1329 additions and 60 deletions

10
.gitattributes vendored
View File

@@ -19076,6 +19076,16 @@ forge-gui/src/main/java/forge/sound/MusicPlaylist.java -text
forge-gui/src/main/java/forge/sound/NoSoundClip.java -text forge-gui/src/main/java/forge/sound/NoSoundClip.java -text
forge-gui/src/main/java/forge/sound/SoundEffectType.java -text forge-gui/src/main/java/forge/sound/SoundEffectType.java -text
forge-gui/src/main/java/forge/sound/SoundSystem.java -text forge-gui/src/main/java/forge/sound/SoundSystem.java -text
forge-gui/src/main/java/forge/tournament/TournamentData.java -text
forge-gui/src/main/java/forge/tournament/TournamentIO.java -text
forge-gui/src/main/java/forge/tournament/TournamentUtil.java -text
forge-gui/src/main/java/forge/tournament/TournamentWinLoseController.java -text
forge-gui/src/main/java/forge/tournament/system/AbstractTournament.java -text
forge-gui/src/main/java/forge/tournament/system/TournamentBracket.java -text
forge-gui/src/main/java/forge/tournament/system/TournamentPairing.java -text
forge-gui/src/main/java/forge/tournament/system/TournamentPlayer.java -text
forge-gui/src/main/java/forge/tournament/system/TournamentRoundRobin.java -text
forge-gui/src/main/java/forge/tournament/system/TournamentSwiss.java -text
forge-gui/src/main/java/forge/util/Callback.java -text forge-gui/src/main/java/forge/util/Callback.java -text
forge-gui/src/main/java/forge/util/Evaluator.java -text forge-gui/src/main/java/forge/util/Evaluator.java -text
forge-gui/src/main/java/forge/util/GuiDisplayUtil.java -text forge-gui/src/main/java/forge/util/GuiDisplayUtil.java -text

View File

@@ -1,5 +1,6 @@
package forge.screens.home.quest; package forge.screens.home.quest;
import com.beust.jcommander.internal.Lists;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import forge.GuiBase; import forge.GuiBase;
import forge.Singletons; import forge.Singletons;
@@ -36,6 +37,9 @@ import forge.toolbox.FOptionPane;
import forge.toolbox.FSkin; import forge.toolbox.FSkin;
import forge.toolbox.FSkin.SkinImage; import forge.toolbox.FSkin.SkinImage;
import forge.toolbox.JXButtonPanel; import forge.toolbox.JXButtonPanel;
import forge.tournament.system.TournamentBracket;
import forge.tournament.system.TournamentPairing;
import forge.tournament.system.TournamentPlayer;
import javax.swing.*; import javax.swing.*;
import java.awt.event.*; import java.awt.event.*;
@@ -438,13 +442,17 @@ public enum CSubmenuQuestDraft implements ICDoc {
} }
private void updateTournamentActive() { private void updateTournamentActive() {
final VSubmenuQuestDraft view = VSubmenuQuestDraft.SINGLETON_INSTANCE;
if (FModel.getQuest().getAchievements().getCurrentDraft() == null) { if (FModel.getQuest().getAchievements().getCurrentDraft() == null) {
return; return;
} }
if (QuestDraftUtils.TOURNAMENT_TOGGLE) {
updateTournamentActiveForBracket();
return;
}
final VSubmenuQuestDraft view = VSubmenuQuestDraft.SINGLETON_INSTANCE;
for (int i = 0; i < 15; i++) { for (int i = 0; i < 15; i++) {
String playerID = FModel.getQuest().getAchievements().getCurrentDraft().getStandings()[i]; String playerID = FModel.getQuest().getAchievements().getCurrentDraft().getStandings()[i];
@@ -493,6 +501,82 @@ public enum CSubmenuQuestDraft implements ICDoc {
} }
private void updateTournamentBoxLabel(String playerID, int iconID, int box, boolean first) {
final VSubmenuQuestDraft view = VSubmenuQuestDraft.SINGLETON_INSTANCE;
SkinImage icon = FSkin.getAvatars().get(iconID);
if (icon == null) {
icon = FSkin.getAvatars().get(0);
}
if (first) {
view.getLblsMatchups()[box].setPlayerOne(playerID, icon);
} else {
view.getLblsMatchups()[box].setPlayerTwo(playerID, icon);
}
}
private void updateTournamentActiveForBracket() {
final VSubmenuQuestDraft view = VSubmenuQuestDraft.SINGLETON_INSTANCE;
QuestEventDraft draft = FModel.getQuest().getAchievements().getCurrentDraft();
TournamentBracket bracket = draft.getBracket();
if (bracket == null) {
return;
}
// Combine finished pairings with active round pairings
List<TournamentPairing> allPairings = Lists.newArrayList();
allPairings.addAll(bracket.getCompletedPairings());
allPairings.addAll(bracket.getActivePairings());
int count = 0;
int playerCount = 0;
int lastWinner = 0;
for(TournamentPairing tp : allPairings) {
boolean first = true;
String playerID = "Undetermined";
int iconID = 0;
for(TournamentPlayer player : tp.getPairedPlayers()) {
if (player.getIndex() == -1) {
playerID = FModel.getPreferences().getPref(FPref.PLAYER_NAME);
if (FModel.getPreferences().getPref(FPref.UI_AVATARS).split(",").length > 0) {
iconID = Integer.parseInt(FModel.getPreferences().getPref(FPref.UI_AVATARS).split(",")[0]);
}
}
else{
playerID = player.getPlayer().getName();
iconID = player.getIndex();
}
updateTournamentBoxLabel(playerID, iconID, count, first);
if (tp.getWinner() != null && tp.getWinner().equals(player)) {
// Temporarily fill in winner box
lastWinner = playerCount;
updateTournamentBoxLabel(player.getPlayer().getName(), player.getIndex(), playerCount/4 + 4, count%2 == 0);
}
first = false;
playerCount++;
}
count++;
}
if (!bracket.isTournamentOver()) {
for (int i = lastWinner/2+9 ; i < 15; i++) {
String playerID = "Undetermined";
int iconID = GuiBase.getInterface().getAvatarCount() - 1;
updateTournamentBoxLabel(playerID, iconID, i / 2, i%2 == 0);
}
}
if (draft.playerHasMatchesLeft()) {
view.getBtnLeaveTournament().setText("Leave Tournament");
} else {
view.getBtnLeaveTournament().setText("Collect Prizes");
}
}
public void setCompletedDraft(final DeckGroup finishedDraft) { public void setCompletedDraft(final DeckGroup finishedDraft) {
QuestDraftUtils.completeDraft(finishedDraft); QuestDraftUtils.completeDraft(finishedDraft);
@@ -587,7 +671,6 @@ public enum CSubmenuQuestDraft implements ICDoc {
gui = GuiBase.getInterface().getNewGuiGame(); gui = GuiBase.getInterface().getNewGuiGame();
QuestDraftUtils.startNextMatch(gui); QuestDraftUtils.startNextMatch(gui);
} }
} }

View File

@@ -70,6 +70,7 @@ public class QuestDraftWinLose extends ControlWinLose {
final String winner = lastGame.getWinningPlayerName(); final String winner = lastGame.getWinningPlayerName();
quest.getAchievements().getCurrentDraft().setWinner(winner); quest.getAchievements().getCurrentDraft().setWinner(winner);
quest.save(); quest.save();
VSubmenuQuestDraft.SINGLETON_INSTANCE.populate();
} }
if (!gameHadHumanPlayer) { if (!gameHadHumanPlayer) {

View File

@@ -1,5 +1,6 @@
package forge.quest; package forge.quest;
import com.google.common.collect.Lists;
import forge.GuiBase; import forge.GuiBase;
import forge.deck.Deck; import forge.deck.Deck;
import forge.deck.DeckGroup; import forge.deck.DeckGroup;
@@ -12,6 +13,9 @@ import forge.match.HostedMatch;
import forge.model.FModel; import forge.model.FModel;
import forge.player.GamePlayerUtil; import forge.player.GamePlayerUtil;
import forge.properties.ForgePreferences.FPref; import forge.properties.ForgePreferences.FPref;
import forge.tournament.system.TournamentBracket;
import forge.tournament.system.TournamentPairing;
import forge.tournament.system.TournamentPlayer;
import forge.util.storage.IStorage; import forge.util.storage.IStorage;
import java.util.ArrayList; import java.util.ArrayList;
@@ -19,6 +23,7 @@ import java.util.List;
public class QuestDraftUtils { public class QuestDraftUtils {
private static final List<DraftMatchup> matchups = new ArrayList<>(); private static final List<DraftMatchup> matchups = new ArrayList<>();
public static boolean TOURNAMENT_TOGGLE = false;
public static boolean matchInProgress = false; public static boolean matchInProgress = false;
private static boolean waitForUserInput = false; private static boolean waitForUserInput = false;
@@ -42,11 +47,11 @@ public class QuestDraftUtils {
} }
public static String getDeckLegality() { public static String getDeckLegality() {
final String message = GameType.QuestDraft.getDeckFormat().getDeckConformanceProblem(FModel.getQuest().getAssets().getDraftDeckStorage().get(QuestEventDraft.DECK_NAME).getHumanDeck()); if (!FModel.getPreferences().getPrefBoolean(FPref.ENFORCE_DECK_LEGALITY)) {
if (message != null && FModel.getPreferences().getPrefBoolean(FPref.ENFORCE_DECK_LEGALITY)) { return null;
return message;
} }
return null;
return GameType.QuestDraft.getDeckFormat().getDeckConformanceProblem(FModel.getQuest().getAssets().getDraftDeckStorage().get(QuestEventDraft.DECK_NAME).getHumanDeck());
} }
private static int getPreviousMatchup(final int position) { private static int getPreviousMatchup(final int position) {
@@ -81,11 +86,35 @@ public class QuestDraftUtils {
return -1; return -1;
} }
public static void startNextMatchInTournament(final IGuiGame gui) {
final QuestEventDraft draft = FModel.getQuest().getAchievements().getCurrentDraft();
TournamentBracket bracket = draft.getBracket();
TournamentPairing pairing = bracket.getNextPairing();
if (pairing == null) {
if (bracket.isTournamentOver()) {
// Somehow tournament didn't end on its own?
} else {
// Generate next round and regrab the next pairing
bracket.generateActivePairings();
pairing = bracket.getNextPairing();
}
}
updateFromTournament(gui);
}
public static void startNextMatch(final IGuiGame gui) { public static void startNextMatch(final IGuiGame gui) {
if (TOURNAMENT_TOGGLE) {
startNextMatchInTournament(gui);
return;
}
if (!matchups.isEmpty()) { if (!matchups.isEmpty()) {
return; return;
} }
// If matchups isn't empty I don't get here. Right?
matchups.clear(); matchups.clear();
final QuestEventDraft draft = FModel.getQuest().getAchievements().getCurrentDraft(); final QuestEventDraft draft = FModel.getQuest().getAchievements().getCurrentDraft();
@@ -130,29 +159,19 @@ public class QuestDraftUtils {
//If no previous matches need doing, start the next round as normal //If no previous matches need doing, start the next round as normal
if (!foundMatchups) { if (!foundMatchups) {
// Fall through to avoid repetition
switch (currentSet) { switch (currentSet) {
case 7: case 7:
addMatchup(0, 1, draft); addMatchup(0, 1, draft);
addMatchup(2, 3, draft);
addMatchup(4, 5, draft);
addMatchup(6, 7, draft);
break;
case 8: case 8:
addMatchup(2, 3, draft); addMatchup(2, 3, draft);
addMatchup(4, 5, draft);
addMatchup(6, 7, draft);
break;
case 9: case 9:
addMatchup(4, 5, draft); addMatchup(4, 5, draft);
addMatchup(6, 7, draft);
break;
case 10: case 10:
addMatchup(6, 7, draft); addMatchup(6, 7, draft);
break; break;
case 11: case 11:
addMatchup(8, 9, draft); addMatchup(8, 9, draft);
addMatchup(10, 11, draft);
break;
case 12: case 12:
addMatchup(10, 11, draft); addMatchup(10, 11, draft);
break; break;
@@ -205,7 +224,22 @@ public class QuestDraftUtils {
matchups.add(matchup); matchups.add(matchup);
} }
private static GameRules createQuestDraftRuleset() {
final GameRules rules = new GameRules(GameType.QuestDraft);
rules.setPlayForAnte(false);
rules.setMatchAnteRarity(false);
rules.setGamesPerMatch(3);
rules.setManaBurn(FModel.getPreferences().getPrefBoolean(FPref.UI_MANABURN));
rules.setCanCloneUseTargetsImage(FModel.getPreferences().getPrefBoolean(FPref.UI_CLONE_MODE_SOURCE));
return rules;
}
public static void update(final IGuiGame gui) { public static void update(final IGuiGame gui) {
if (TOURNAMENT_TOGGLE) {
updateFromTournament(gui);
return;
}
if (matchups.isEmpty()) { if (matchups.isEmpty()) {
return; return;
} }
@@ -226,17 +260,71 @@ public class QuestDraftUtils {
waitForUserInput = false; waitForUserInput = false;
} }
final GameRules rules = new GameRules(GameType.QuestDraft); GameRules rules = createQuestDraftRuleset();
rules.setPlayForAnte(false);
rules.setMatchAnteRarity(false);
rules.setGamesPerMatch(3);
rules.setManaBurn(FModel.getPreferences().getPrefBoolean(FPref.UI_MANABURN));
rules.setCanCloneUseTargetsImage(FModel.getPreferences().getPrefBoolean(FPref.UI_CLONE_MODE_SOURCE));
final HostedMatch newMatch = GuiBase.getInterface().hostMatch(); final HostedMatch newMatch = GuiBase.getInterface().hostMatch();
newMatch.startMatch(rules, null, nextMatch.matchStarter, nextMatch.humanPlayer, GuiBase.getInterface().getNewGuiGame()); newMatch.startMatch(rules, null, nextMatch.matchStarter, nextMatch.humanPlayer, GuiBase.getInterface().getNewGuiGame());
} }
private static List<RegisteredPlayer> registerTournamentPlayers(TournamentPairing pairing, QuestEventDraft draft, DeckGroup decks) {
List<RegisteredPlayer> registered = Lists.newArrayList();
for (TournamentPlayer pl : pairing.getPairedPlayers()) {
if (pl.getIndex() == -1) {
registered.add(new RegisteredPlayer(decks.getHumanDeck()).setPlayer(pl.getPlayer()));
} else {
registered.add(new RegisteredPlayer(decks.getAiDecks().get(pl.getIndex())).setPlayer(pl.getPlayer()));
}
}
return registered;
}
private static void updateFromTournament(final IGuiGame gui) {
// If Human involved launch into a UI, if not show a "Simulating" screen. And simulate the game off-thread
if (waitForUserInput || matchInProgress || gui == null) {
return;
}
gui.enableOverlay();
final QuestEventDraft draft = FModel.getQuest().getAchievements().getCurrentDraft();
if (draft == null) {
return;
}
TournamentBracket bracket = draft.getBracket();
TournamentPairing pairing = bracket.getNextPairing();
if (pairing == null) {
// bracket.isTournamentOver()
gui.disableOverlay();
return;
}
matchInProgress = true;
final DeckGroup decks = FModel.getQuest().getAssets().getDraftDeckStorage().get(QuestEventDraft.DECK_NAME);
boolean waitForUserInput = pairing.hasPlayer(GamePlayerUtil.getGuiPlayer());
GameRules rules = createQuestDraftRuleset();
List<RegisteredPlayer> registered = registerTournamentPlayers(pairing, draft, decks);
RegisteredPlayer registeredHuman = null;
if (waitForUserInput) {
final HostedMatch newMatch = GuiBase.getInterface().hostMatch();
for(RegisteredPlayer rp : registered) {
if (rp.getPlayer().equals(GamePlayerUtil.getGuiPlayer())) {
registeredHuman = rp;
}
}
newMatch.startMatch(rules, null, registered, registeredHuman, GuiBase.getInterface().getNewGuiGame());
} else {
// TODO Show a "Simulating Dialog" and simulate off-thread. Temporary replication of code for now
gui.disableOverlay();
final HostedMatch newMatch = GuiBase.getInterface().hostMatch();
newMatch.startMatch(rules, null, registered, registeredHuman, GuiBase.getInterface().getNewGuiGame());
}
}
public static void continueMatches(final IGuiGame gui) { public static void continueMatches(final IGuiGame gui) {
waitForUserInput = false; waitForUserInput = false;
update(gui); update(gui);
@@ -246,6 +334,11 @@ public class QuestDraftUtils {
matchInProgress = false; matchInProgress = false;
waitForUserInput = false; waitForUserInput = false;
matchups.clear(); matchups.clear();
if (TOURNAMENT_TOGGLE) {
final QuestEventDraft draft = FModel.getQuest().getAchievements().getCurrentDraft();
TournamentBracket bracket = draft.getBracket();
bracket.endTournament();
}
} }
private static final class DraftMatchup { private static final class DraftMatchup {

View File

@@ -33,6 +33,9 @@ import forge.model.FModel;
import forge.player.GamePlayerUtil; import forge.player.GamePlayerUtil;
import forge.quest.data.QuestPreferences.QPref; import forge.quest.data.QuestPreferences.QPref;
import forge.quest.io.ReadPriceList; import forge.quest.io.ReadPriceList;
import forge.tournament.system.TournamentBracket;
import forge.tournament.system.TournamentPairing;
import forge.tournament.system.TournamentPlayer;
import forge.util.NameGenerator; import forge.util.NameGenerator;
import forge.util.storage.IStorage; import forge.util.storage.IStorage;
@@ -79,6 +82,8 @@ public class QuestEventDraft {
public static final String UNDETERMINED = "quest_draft_undetermined_place"; public static final String UNDETERMINED = "quest_draft_undetermined_place";
public static final String HUMAN = "quest_draft_human_place"; public static final String HUMAN = "quest_draft_human_place";
public static final String DECK_NAME = "Tournament Deck"; public static final String DECK_NAME = "Tournament Deck";
public static final int TOTAL_ROUNDS = 3;
public static final int PLAYERS_IN_PAIRING = 2;
private static transient final ReadPriceList PRICE_LIST_READER = new ReadPriceList(); private static transient final ReadPriceList PRICE_LIST_READER = new ReadPriceList();
private static transient final Map<String, Integer> MAP_PRICES = PRICE_LIST_READER.getPriceList(); private static transient final Map<String, Integer> MAP_PRICES = PRICE_LIST_READER.getPriceList();
@@ -90,6 +95,8 @@ public class QuestEventDraft {
private int entryFee = 0; private int entryFee = 0;
private String[] standings = new String[15]; private String[] standings = new String[15];
private TournamentBracket bracket;
private String[] aiNames = new String[7]; private String[] aiNames = new String[7];
private int[] aiIcons = new int[7]; private int[] aiIcons = new int[7];
@@ -169,6 +176,10 @@ public class QuestEventDraft {
age--; age--;
} }
public TournamentBracket getBracket() { return bracket; }
public void setBracket(TournamentBracket brckt) { bracket = brckt; }
public void saveToRegularDraft() { public void saveToRegularDraft() {
final String tournamentName = FModel.getQuest().getName() + " Tournament Deck " + new SimpleDateFormat("EEE d MMM yyyy HH-mm-ss").format(new Date()); final String tournamentName = FModel.getQuest().getName() + " Tournament Deck " + new SimpleDateFormat("EEE d MMM yyyy HH-mm-ss").format(new Date());
final DeckGroup original = FModel.getQuest().getDraftDecks().get(QuestEventDraft.DECK_NAME); final DeckGroup original = FModel.getQuest().getDraftDecks().get(QuestEventDraft.DECK_NAME);
@@ -208,6 +219,18 @@ public class QuestEventDraft {
public void setWinner(final String playerName) { public void setWinner(final String playerName) {
if (QuestDraftUtils.TOURNAMENT_TOGGLE) {
TournamentPairing pairing = bracket.getNextPairing();
for(TournamentPlayer player : pairing.getPairedPlayers()) {
if (player.getPlayer().getName().equals(playerName)) {
pairing.setWinner(player);
bracket.reportMatchCompletion(pairing);
break;
}
}
// Since this updates the brackets that we're still pseudo-using let it fall through
}
boolean isHumanPlayer = true; boolean isHumanPlayer = true;
for (final String name : aiNames) { for (final String name : aiNames) {
@@ -245,37 +268,7 @@ public class QuestEventDraft {
} }
switch (playerIndex) { standings[playerIndex/2+8] = standings[playerIndex];
case 0:
case 1:
standings[8] = standings[playerIndex];
break;
case 2:
case 3:
standings[9] = standings[playerIndex];
break;
case 4:
case 5:
standings[10] = standings[playerIndex];
break;
case 6:
case 7:
standings[11] = standings[playerIndex];
break;
case 8:
case 9:
standings[12] = standings[playerIndex];
break;
case 10:
case 11:
standings[13] = standings[playerIndex];
break;
case 12:
case 13:
standings[14] = standings[playerIndex];
break;
}
} }
/** /**
@@ -529,6 +522,10 @@ public class QuestEventDraft {
public boolean playerHasMatchesLeft() { public boolean playerHasMatchesLeft() {
if (QuestDraftUtils.TOURNAMENT_TOGGLE) {
return !bracket.isTournamentOver() && bracket.isPlayerRemaining(-1);
}
int playerIndex = -1; int playerIndex = -1;
for (int i = standings.length - 1; i >= 0; i--) { for (int i = standings.length - 1; i >= 0; i--) {
if (standings[i].equals(HUMAN)) { if (standings[i].equals(HUMAN)) {
@@ -581,6 +578,13 @@ public class QuestEventDraft {
} }
public int getPlayerPlacement() { public int getPlayerPlacement() {
// "1st Place" and "2nd Place" are accurate
// "3rd Place" is the two remaining players who won in the first round
// "4th Place" is anyone who lost in the first round
if (QuestDraftUtils.TOURNAMENT_TOGGLE) {
return 5 - bracket.getFurthestRound(-1);
}
int playerIndex = -1; int playerIndex = -1;
for (int i = standings.length - 1; i >= 0; i--) { for (int i = standings.length - 1; i >= 0; i--) {
@@ -896,6 +900,7 @@ public class QuestEventDraft {
Collections.shuffle(players); Collections.shuffle(players);
// Initialize tournament
for (int i = 0; i < players.size(); i++) { for (int i = 0; i < players.size(); i++) {
event.standings[i] = players.get(i); event.standings[i] = players.get(i);
} }
@@ -930,6 +935,8 @@ public class QuestEventDraft {
} }
event.bracket = createBracketFromStandings(event.standings, event.aiNames, event.aiIcons);
return event; return event;
} }
@@ -1040,4 +1047,49 @@ public class QuestEventDraft {
} }
public static TournamentBracket createBracketFromStandings(String[] standings, String[] aiNames, int[] aiIcons) {
TournamentBracket bracket = new TournamentBracket(TOTAL_ROUNDS, PLAYERS_IN_PAIRING);
bracket.setContinualPairing(false);
int roundParticipants = (int)(Math.pow(PLAYERS_IN_PAIRING, TOTAL_ROUNDS));
int i;
// Initialize Players
for(i = 0; i < roundParticipants; i++) {
if (standings[i].equals(HUMAN)) {
bracket.addTournamentPlayer(GamePlayerUtil.getGuiPlayer());
} else {
int idx = Integer.valueOf(standings[i]) - 1;
bracket.addTournamentPlayer(GamePlayerUtil.createAiPlayer(aiNames[idx], aiIcons[idx]), idx);
}
}
bracket.setInitialized(true);
bracket.generateActivePairings();
while(i < 14) {
TournamentPairing pairing = bracket.getNextPairing();
if (pairing == null) {
// No pairings available. Generate next round and continue
bracket.generateActivePairings();
pairing = bracket.getNextPairing();
// Pairing really shouldn't be null at this point
}
if (standings[i].equals(UNDETERMINED)) {
// Bracket now up to date!
break;
} else {
int idx = standings[i].equals(HUMAN) ? -1 : Integer.valueOf(standings[i]) - 1;
pairing.setWinnerByIndex(idx);
bracket.reportMatchCompletion(pairing);
}
i += 1;
}
if (i == 14) {
// Tournament has ended! Do I have to do anything?
System.out.println("Tournament done...");
}
return bracket;
}
} }

View File

@@ -284,7 +284,14 @@ public class QuestAchievements {
FModel.getQuest().getDraftDecks().delete(QuestEventDraft.DECK_NAME); FModel.getQuest().getDraftDecks().delete(QuestEventDraft.DECK_NAME);
return null; return null;
} }
return drafts.get(currentDraft);
try {
return drafts.get(currentDraft);
}
catch(ArrayIndexOutOfBoundsException e) {
return null;
}
} }
public int getCurrentDraftIndex() { public int getCurrentDraftIndex() {

View File

@@ -198,6 +198,10 @@ public class QuestDataIO {
QuestDataIO.setFinalField(QuestData.class, "matchLength", newData, 3); QuestDataIO.setFinalField(QuestData.class, "matchLength", newData, 3);
} }
if (saveVersion < 11) {
// Migrate DraftTournaments to use new Tournament class
}
final QuestAssets qS = newData.getAssets(); final QuestAssets qS = newData.getAssets();
final QuestAchievements qA = newData.getAchievements(); final QuestAchievements qA = newData.getAchievements();
@@ -499,7 +503,6 @@ public class QuestDataIO {
QuestEventDraftContainer drafts = (QuestEventDraftContainer) source; QuestEventDraftContainer drafts = (QuestEventDraftContainer) source;
for (QuestEventDraft draft : drafts) { for (QuestEventDraft draft : drafts) {
writer.startNode("draft"); writer.startNode("draft");
writer.startNode("title"); writer.startNode("title");
@@ -534,6 +537,11 @@ public class QuestDataIO {
} }
writer.endNode(); writer.endNode();
// TODO Save bracket instead of standings
//writer.startNode("bracket");
//draft.getBracket().exportToXML(writer);
//writer.endNode();
writer.startNode("aiNames"); writer.startNode("aiNames");
i = 0; i = 0;
for (String name : draft.getAINames()) { for (String name : draft.getAINames()) {
@@ -575,13 +583,13 @@ public class QuestDataIO {
while (reader.hasMoreChildren()) { while (reader.hasMoreChildren()) {
reader.moveDown(); reader.moveDown();
// TODO Add Tournament
String draftName = null; String draftName = null;
String boosterConfiguration = null; String boosterConfiguration = null;
int entryFee = 1500; int entryFee = 1500;
int age = 15; int age = 15;
String block = null; String block = null;
String[] standings = new String[15]; String[] standings = null;
String[] aiNames = new String[7]; String[] aiNames = new String[7];
int[] aiIcons = new int[7]; int[] aiIcons = new int[7];
boolean started = false; boolean started = false;
@@ -604,6 +612,8 @@ public class QuestDataIO {
block = reader.getValue(); block = reader.getValue();
break; break;
case "standings": case "standings":
// TODO Leaving for older quest datas, but will convert immediately to bracket
standings = new String[15];
int i = 0; int i = 0;
while (reader.hasMoreChildren()) { while (reader.hasMoreChildren()) {
reader.moveDown(); reader.moveDown();
@@ -611,6 +621,9 @@ public class QuestDataIO {
reader.moveUp(); reader.moveUp();
} }
break; break;
case "bracket":
// TODO For newer quest datas, that store brackets
break;
case "aiNames": case "aiNames":
int ii = 0; int ii = 0;
while (reader.hasMoreChildren()) { while (reader.hasMoreChildren()) {
@@ -643,7 +656,11 @@ public class QuestDataIO {
draft.setBoosterConfiguration(boosterConfiguration); draft.setBoosterConfiguration(boosterConfiguration);
draft.setEntryFee(entryFee); draft.setEntryFee(entryFee);
draft.setBlock(block); draft.setBlock(block);
// TODO Stop setting standings once Bracket is loading from IO
draft.setStandings(standings); draft.setStandings(standings);
if (standings != null) {
draft.setBracket(QuestEventDraft.createBracketFromStandings(standings, aiNames, aiIcons));
}
draft.setAINames(aiNames); draft.setAINames(aiNames);
draft.setAIIcons(aiIcons); draft.setAIIcons(aiIcons);
draft.setStarted(started); draft.setStarted(started);

View File

@@ -0,0 +1,148 @@
package forge.tournament;
import com.thoughtworks.xstream.annotations.XStreamOmitField;
import forge.GuiBase;
import forge.deck.Deck;
import forge.game.GameType;
import forge.game.player.RegisteredPlayer;
import forge.match.HostedMatch;
import forge.properties.ForgeConstants;
import java.io.File;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class TournamentData {
@XStreamOmitField
private String name; // set based on the the filename on load
private transient HostedMatch hostedMatch = null;
private int completed;
private String timestamp;
private List<String> eventRecords = new ArrayList<String>();
private List<String> eventNames = new ArrayList<String>();
private Deck userDeck;
private List<Deck> decks;
public TournamentData() {
}
public void setName(String name0) {
name = name0;
}
public void rename(final String newName) {
File newpath = new File(ForgeConstants.GAUNTLET_DIR.userPrefLoc, newName + ".dat");
File oldpath = new File(ForgeConstants.GAUNTLET_DIR.userPrefLoc, name + ".dat");
oldpath.renameTo(newpath);
name = newName;
TournamentIO.saveTournament(this);
}
public String getName() {
return name;
}
public String getDisplayName() {
if (name.startsWith(TournamentIO.PREFIX_LOCKED)) { //trim locked prefix if needed
return name.substring(TournamentIO.PREFIX_LOCKED.length());
}
return name;
}
public void stamp() {
final DateFormat dateFormat = new SimpleDateFormat("MM-dd-yy, H:m");
timestamp = dateFormat.format(new Date()).toString();
}
/** Resets a Tournament data to an unplayed state, then stamps and saves. */
public void reset() {
completed = 0;
stamp();
eventRecords.clear();
for (int i = 0; i < decks.size(); i++) {
eventRecords.add("");
}
TournamentIO.saveTournament(this);
}
public String getTimestamp() {
return timestamp;
}
public void setCompleted(final int i0) {
completed = i0;
}
public int getCompleted() {
return completed;
}
public void setUserDeck(final Deck d0) {
userDeck = d0;
}
public Deck getUserDeck() {
return userDeck;
}
public List<String> getDeckNames() {
final List<String> names = new ArrayList<String>();
for (final Deck d : decks) { names.add(d.getName()); }
return names;
}
public void setEventRecords(final List<String> records0) {
eventRecords = records0;
}
public List<String> getEventRecords() {
return eventRecords;
}
public void setEventNames(final List<String> names0) {
eventNames = names0;
}
public List<String> getEventNames() {
return eventNames;
}
public void setDecks(final List<Deck> decks0) {
decks = decks0;
}
public List<Deck> getDecks() {
return decks;
}
public void startRound(final List<RegisteredPlayer> players, final RegisteredPlayer human) {
hostedMatch = GuiBase.getInterface().hostMatch();
hostedMatch.startMatch(GameType.Tournament, null, players, human, GuiBase.getInterface().getNewGuiGame());
}
public void nextRound(final List<RegisteredPlayer> players, final RegisteredPlayer human) {
if (hostedMatch == null) {
throw new IllegalStateException("Cannot advance round when no match has been hosted.");
}
hostedMatch.endCurrentGame();
startRound(players, human);
}
@Override
public String toString() {
String str = getDisplayName();
if (decks != null) {
str += " (" + decks.size() + " opponents)";
}
return str;
}
}

View File

@@ -0,0 +1,190 @@
package forge.tournament;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import forge.deck.CardPool;
import forge.item.PaperCard;
import forge.model.FModel;
import forge.properties.ForgeConstants;
import forge.util.IgnoringXStream;
import org.apache.commons.lang3.StringUtils;
import java.io.*;
import java.util.Map;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
public class TournamentIO {
/** Prompt in text field for new (unsaved) built gauntlets. */
public static final String TXF_PROMPT = "[New Tournament]";
/** suffix for all gauntlet data files */
public static final String SUFFIX_DATA = ".dat";
/** Prefix for quick tournament save files. */
public static final String PREFIX_QUICK = "Quick_";
/** Prefix for custom tournament save files. */
public static final String PREFIX_CUSTOM = "Custom_";
/** Regex for locked tournament save files. */
public static final String PREFIX_LOCKED = "LOCKED_";
protected static XStream getSerializer(final boolean isIgnoring) {
final XStream xStream = isIgnoring ? new IgnoringXStream() : new XStream();
xStream.registerConverter(new DeckSectionToXml());
xStream.autodetectAnnotations(true);
return xStream;
}
public static File getTournamentFile(final String name) {
return new File(ForgeConstants.TOURNAMENT_DIR.userPrefLoc, name + SUFFIX_DATA);
}
public static File getTournamentFile(final TournamentData gd) {
return getTournamentFile(gd.getName());
}
public static File[] getTournamentFilesUnlocked(final String prefix) {
final FilenameFilter filter = new FilenameFilter() {
@Override
public boolean accept(final File dir, final String name) {
return ((prefix == null || name.startsWith(prefix)) && name.endsWith(SUFFIX_DATA));
}
};
final File folder = new File(ForgeConstants.TOURNAMENT_DIR.userPrefLoc);
return folder.listFiles(filter);
}
public static File[] getTournamentFilesLocked() {
final FilenameFilter filter = new FilenameFilter() {
@Override
public boolean accept(final File dir, final String name) {
return (name.startsWith(PREFIX_LOCKED) && name.endsWith(SUFFIX_DATA));
}
};
final File folder = new File(ForgeConstants.TOURNAMENT_DIR.defaultLoc);
return folder.listFiles(filter);
}
public static TournamentData loadTournament(final File xmlSaveFile) {
GZIPInputStream zin = null;
boolean isCorrupt = false;
try {
zin = new GZIPInputStream(new FileInputStream(xmlSaveFile));
final InputStreamReader reader = new InputStreamReader(zin);
final TournamentData data = (TournamentData)TournamentIO.getSerializer(true).fromXML(reader);
final String filename = xmlSaveFile.getName();
data.setName(filename.substring(0, filename.length() - SUFFIX_DATA.length()));
return data;
}
catch (final IOException e) {
e.printStackTrace();
}
catch (final Exception e) { //if there's a non-IO exception, delete the corrupt file
e.printStackTrace();
isCorrupt = true;
}
finally {
if (zin != null) {
try {
zin.close();
} catch (final IOException e) {
System.out.println("error closing tournament data reader: " + e);
}
}
}
if (isCorrupt) {
try {
xmlSaveFile.delete();
} catch (final Exception e) {
System.out.println("error delete corrupt tournament file: " + e);
}
}
return null;
}
public static void saveTournament(final TournamentData gd0) {
try {
final XStream xStream = TournamentIO.getSerializer(false);
TournamentIO.savePacked(xStream, gd0);
} catch (final Exception ex) {
throw new RuntimeException(ex);
}
}
private static void savePacked(final XStream xStream0, final TournamentData gd0) throws IOException {
final BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(getTournamentFile(gd0)));
final GZIPOutputStream zout = new GZIPOutputStream(bout);
xStream0.toXML(gd0, zout);
zout.flush();
zout.close();
}
private static class DeckSectionToXml implements Converter {
@SuppressWarnings("rawtypes")
@Override
public boolean canConvert(final Class clasz) {
return clasz.equals(CardPool.class);
}
@Override
public void marshal(final Object source, final HierarchicalStreamWriter writer, final MarshallingContext context) {
for (final Map.Entry<PaperCard, Integer> e : (CardPool) source) {
writeCardPrinted(e.getKey(), e.getValue(), writer);
}
}
@Override
public Object unmarshal(final HierarchicalStreamReader reader, final UnmarshallingContext context) {
final CardPool result = new CardPool();
while (reader.hasMoreChildren()) {
reader.moveDown();
final String sCnt = reader.getAttribute("n");
final int cnt = StringUtils.isNumeric(sCnt) ? Integer.parseInt(sCnt) : 1;
final String nodename = reader.getNodeName();
if ("string".equals(nodename)) {
result.add(FModel.getMagicDb().getCommonCards().getCard(reader.getValue()));
} else if ("card".equals(nodename)) { // new format
result.add(readCardPrinted(reader), cnt);
}
reader.moveUp();
}
return result;
}
private static void writeCardPrinted(final PaperCard cref, final Integer count, final HierarchicalStreamWriter writer) {
writer.startNode("card");
writer.addAttribute("c", cref.getName());
writer.addAttribute("s", cref.getEdition());
if (cref.isFoil()) {
writer.addAttribute("foil", "1");
}
writer.addAttribute("i", Integer.toString(cref.getArtIndex()));
writer.addAttribute("n", count.toString());
writer.endNode();
}
private static PaperCard readCardPrinted(final HierarchicalStreamReader reader) {
final String name = reader.getAttribute("c");
final String set = reader.getAttribute("s");
final String sIndex = reader.getAttribute("i");
final short index = StringUtils.isNumeric(sIndex) ? Short.parseShort(sIndex) : 0;
final boolean foil = "1".equals(reader.getAttribute("foil"));
PaperCard card = FModel.getMagicDb().getCommonCards().getCard(name, set, index);
if (null == card) {
card = FModel.getMagicDb().getCommonCards().getCard(name, set, -1);
}
if (null == card) {
throw new RuntimeException("Unsupported card found in quest save: " + name + " from edition " + set);
}
return foil ? FModel.getMagicDb().getCommonCards().getFoiled(card) : card;
}
}
}

View File

@@ -0,0 +1,76 @@
package forge.tournament;
import forge.deck.Deck;
import forge.deck.DeckType;
import forge.deck.DeckgenUtil;
import forge.model.FModel;
import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class TournamentUtil {
public static TournamentData createQuickTournament(final Deck userDeck, final int numOpponents, final List<DeckType> allowedDeckTypes) {
TournamentData tournament = new TournamentData();
setDefaultTournamentName(tournament, TournamentIO.PREFIX_QUICK);
FModel.setTournamentData(tournament);
// Generate tournament decks
Deck deck;
final List<String> eventNames = new ArrayList<String>();
final List<Deck> decks = new ArrayList<Deck>();
for (int i = 0; i < numOpponents; i++) {
int randType = (int)Math.floor(Math.random() * allowedDeckTypes.size());
switch (allowedDeckTypes.get(randType)) {
case COLOR_DECK:
deck = DeckgenUtil.getRandomColorDeck(true);
eventNames.add("Random colors deck");
break;
case CUSTOM_DECK:
deck = DeckgenUtil.getRandomCustomDeck();
eventNames.add(deck.getName());
break;
case PRECONSTRUCTED_DECK:
deck = DeckgenUtil.getRandomPreconDeck();
eventNames.add(deck.getName());
break;
case QUEST_OPPONENT_DECK:
deck = DeckgenUtil.getRandomQuestDeck();
eventNames.add(deck.getName());
break;
case THEME_DECK:
deck = DeckgenUtil.getRandomThemeDeck();
eventNames.add(deck.getName());
break;
default:
continue;
}
decks.add(deck);
}
tournament.setDecks(decks);
tournament.setEventNames(eventNames);
tournament.setUserDeck(userDeck);
// Reset all variable fields to 0, stamps and saves automatically.
tournament.reset();
return tournament;
}
public static void setDefaultTournamentName(TournamentData tournament, String prefix) {
final File[] arrFiles = TournamentIO.getTournamentFilesUnlocked(prefix);
final Set<String> setNames = new HashSet<String>();
for (File f : arrFiles) {
setNames.add(f.getName());
}
int num = 1;
while (setNames.contains(prefix + num + TournamentIO.SUFFIX_DATA)) {
num++;
}
tournament.setName(prefix + num);
}
}

View File

@@ -0,0 +1,132 @@
package forge.tournament;
import com.google.common.collect.Lists;
import forge.LobbyPlayer;
import forge.assets.FSkinProp;
import forge.deck.Deck;
import forge.game.GameView;
import forge.game.player.RegisteredPlayer;
import forge.interfaces.IButton;
import forge.interfaces.IWinLoseView;
import forge.model.FModel;
import forge.player.GamePlayerUtil;
import java.util.List;
abstract public class TournamentWinLoseController {
private final IWinLoseView<? extends IButton> view;
private final GameView lastGame;
public TournamentWinLoseController(IWinLoseView<? extends IButton> view0, final GameView game0) {
view = view0;
lastGame = game0;
}
public void showOutcome() {
final TournamentData gd = FModel.getTournamentData();
final List<String> lstEventNames = gd.getEventNames();
final List<Deck> lstDecks = gd.getDecks();
final List<String> lstEventRecords = gd.getEventRecords();
final int len = lstDecks.size();
final int num = gd.getCompleted();
FSkinProp icon = null;
String message1 = null;
String message2 = null;
// No restarts.
view.getBtnRestart().setVisible(false);
// Generic event record.
lstEventRecords.set(gd.getCompleted(), "Ongoing");
// Match won't be saved until it is over. This opens up a cheat
// or failsafe mechanism (depending on your perspective) in which
// the player can restart Forge to replay a match.
// Pretty sure this can't be fixed until in-game states can be
// saved. Doublestrike 07-10-12
LobbyPlayer questPlayer = GamePlayerUtil.getGuiPlayer();
// In all cases, update stats.
lstEventRecords.set(gd.getCompleted(), lastGame.getGamesWonBy(questPlayer) + " - "
+ (lastGame.getNumPlayedGamesInMatch() - lastGame.getGamesWonBy(questPlayer) + 1));
boolean isMatchOver = lastGame.isMatchOver();
if (isMatchOver) {
gd.setCompleted(gd.getCompleted() + 1);
// Win match case
if (lastGame.isMatchWonBy(questPlayer)) {
// Tournament complete: Remove save file
if (gd.getCompleted() == lstDecks.size()) {
icon = FSkinProp.ICO_QUEST_COIN;
message1 = "CONGRATULATIONS!";
message2 = "You made it through the tournament!";
view.getBtnContinue().setVisible(false);
view.getBtnQuit().setText("OK");
// Remove save file if it's a quickie, or just reset it.
if (gd.getName().startsWith(TournamentIO.PREFIX_QUICK)) {
TournamentIO.getTournamentFile(gd).delete();
}
else {
gd.reset();
}
}
// Or, save and move to next game
else {
gd.stamp();
TournamentIO.saveTournament(gd);
view.getBtnContinue().setText("Next Round (" + (gd.getCompleted() + 1)
+ "/" + len + ")");
view.getBtnContinue().setVisible(true);
view.getBtnContinue().setEnabled(true);
view.getBtnQuit().setText("Save and Quit");
}
}
// Lose match case; stop tournament.
else {
icon = FSkinProp.ICO_QUEST_HEART;
message1 = "DEFEATED!";
message2 = "You have failed to pass the tournament.";
view.getBtnContinue().setVisible(false);
// Remove save file if it's a quickie, or just reset it.
if (gd.getName().startsWith(TournamentIO.PREFIX_QUICK)) {
TournamentIO.getTournamentFile(gd).delete();
}
else {
gd.reset();
}
}
}
gd.setEventRecords(lstEventRecords);
showOutcome(isMatchOver, message1, message2, icon, lstEventNames, lstEventRecords, len, num);
}
public final boolean actionOnContinue() {
if (lastGame.isMatchOver()) {
// To change the AI deck, we have to create a new match.
final TournamentData gd = FModel.getTournamentData();
final RegisteredPlayer human = new RegisteredPlayer(gd.getUserDeck()).setPlayer(GamePlayerUtil.getGuiPlayer());
final Deck aiDeck = gd.getDecks().get(gd.getCompleted());
final List<RegisteredPlayer> players = Lists.newArrayList();
players.add(human);
players.add(new RegisteredPlayer(aiDeck).setPlayer(GamePlayerUtil.createAiPlayer()));
view.hide();
saveOptions();
gd.nextRound(players, human);
return true;
}
return false;
}
protected abstract void showOutcome(boolean isMatchOver, String message1, String message2, FSkinProp icon, List<String> lstEventNames, List<String> lstEventRecords, int len, int num);
protected abstract void saveOptions();
}

View File

@@ -0,0 +1,161 @@
package forge.tournament.system;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import forge.LobbyPlayer;
import forge.player.GamePlayerUtil;
import forge.util.MyRandom;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public abstract class AbstractTournament implements Serializable {
protected int activeRound;
protected int totalRounds;
protected int playersInPairing = 2;
protected boolean initialized = false;
protected boolean continualPairing = true;
protected final List<TournamentPlayer> allPlayers = new ArrayList<TournamentPlayer>();
protected transient final List<TournamentPlayer> remainingPlayers = new ArrayList<TournamentPlayer>();
protected final List<TournamentPairing> completedPairings = new ArrayList<>();
protected final List<TournamentPairing> activePairings = new ArrayList<>();
public List<TournamentPairing> getCompletedPairings() { return completedPairings; }
public List<TournamentPairing> getActivePairings() { return activePairings; }
public AbstractTournament(int ttlRnds) {
activeRound = 0;
totalRounds = ttlRnds;
}
public AbstractTournament(int ttlRnds, List<TournamentPlayer> plrs) {
activeRound = 0;
totalRounds = ttlRnds;
allPlayers.addAll(plrs);
remainingPlayers.addAll(plrs);
}
public void initializeTournament() {
// "Randomly" seed players to start tournament
Collections.shuffle(remainingPlayers, MyRandom.getRandom());
generateActivePairings();
initialized = true;
}
public TournamentPairing getNextPairing() {
if (activePairings.isEmpty()) {
return null;
}
return activePairings.get(0);
}
public boolean isContinualPairing() { return continualPairing; }
public void setContinualPairing(boolean continualPairing) { this.continualPairing = continualPairing; }
public boolean isInitialized() { return initialized; }
public void setInitialized(boolean initialized) { this.initialized = initialized; }
public boolean isPlayerRemaining(TournamentPlayer player) {
return remainingPlayers.contains(player);
}
public boolean isPlayerRemaining(int index) {
for(TournamentPlayer player : remainingPlayers) {
if (player.getIndex() == index) {
return true;
}
}
return false;
}
abstract public void generateActivePairings();
abstract public void reportMatchCompletion(TournamentPairing pairing);
abstract public boolean completeRound();
public void finishMatch(TournamentPairing pairing) {
activePairings.remove(pairing);
completedPairings.add(pairing);
}
abstract public void endTournament();
public boolean isTournamentOver() {
return (initialized && activeRound == totalRounds && activePairings.isEmpty());
}
public void addTournamentPlayer(LobbyPlayer pl) {
TournamentPlayer player = new TournamentPlayer(pl);
allPlayers.add(player);
remainingPlayers.add(player);
}
public void addTournamentPlayer(LobbyPlayer pl, int idx) {
TournamentPlayer player = new TournamentPlayer(pl, idx);
allPlayers.add(player);
remainingPlayers.add(player);
}
public void createTournamentPlayersForDraft(String[] names, int[] icons) {
int size = names.length;
for(int i = 0; i < size; i++) {
TournamentPlayer player = new TournamentPlayer(GamePlayerUtil.createAiPlayer(names[i], icons[i]), i);
allPlayers.add(player);
remainingPlayers.add(player);
}
}
// Probably should be a interface here, not a standalone function
public void exportToXML(HierarchicalStreamWriter writer) {
/*
protected int activeRound;
protected int totalRounds;
protected int playersInPairing = 2;
protected boolean initialized = false;
protected final List<TournamentPlayer> allPlayers = new ArrayList<TournamentPlayer>();
protected final List<TournamentPairing> completedPairings = new ArrayList<>();
protected final List<TournamentPairing> activePairings = new ArrayList<>();
*/
writer.startNode("activeRound");
writer.setValue(Integer.toString(activeRound));
writer.endNode();
writer.startNode("totalRounds");
writer.setValue(Integer.toString(totalRounds));
writer.endNode();
writer.startNode("playersInPairing");
writer.setValue(Integer.toString(playersInPairing));
writer.endNode();
writer.startNode("initialized");
writer.setValue(Boolean.toString(initialized));
writer.endNode();
writer.startNode("allPlayers");
for (TournamentPlayer player : allPlayers) {
writer.startNode("tournamentPlayer");
writer.setValue(player.toString());
writer.endNode();
}
writer.endNode();
writer.startNode("completedPairings");
for (TournamentPairing pairing : completedPairings) {
writer.startNode("tournamentPairing");
pairing.exportToXML(writer);
writer.endNode();
}
writer.endNode();
writer.startNode("completedPairings");
for (TournamentPairing pairing : activePairings) {
writer.startNode("tournamentPairing");
pairing.exportToXML(writer);
writer.endNode();
}
writer.endNode();
}
}

View File

@@ -0,0 +1,104 @@
package forge.tournament.system;
import java.util.ArrayList;
import java.util.List;
public class TournamentBracket extends AbstractTournament {
// Bracket implies single elimination. For non-single elimination, use Swiss or RoundRobin
public TournamentBracket(int ttlRnds, int pairingAmount) {
super(ttlRnds);
this.playersInPairing = pairingAmount;
// Don't initialize the tournament if no players are available
}
public TournamentBracket(int ttlRnds, List<TournamentPlayer> allPlayers) {
super(ttlRnds, allPlayers);
initializeTournament();
}
public TournamentBracket(int ttlRnds, List<TournamentPlayer> allPlayers, int pairingAmount) {
super(ttlRnds, allPlayers);
this.playersInPairing = pairingAmount;
initializeTournament();
}
@Override
public void generateActivePairings() {
activeRound++;
List<TournamentPlayer> pair = new ArrayList<>();
int count = 0;
for (TournamentPlayer tp : this.remainingPlayers) {
pair.add(tp);
count++;
if (count == this.playersInPairing) {
count = 0;
activePairings.add(new TournamentPairing(activeRound, pair));
pair = new ArrayList<>();
}
}
if (count >= 1) {
// Leftover players. Really shouldn't happen in a Bracket.
TournamentPairing pairing = new TournamentPairing(activeRound, pair);
if (count == 1) {
pairing.setBye(true);
}
activePairings.add(pairing);
}
}
@Override
public void reportMatchCompletion(TournamentPairing pairing) {
finishMatch(pairing);
for (TournamentPlayer tp : pairing.getPairedPlayers()) {
if (!tp.equals(pairing.getWinner())) {
remainingPlayers.remove(tp);
tp.setActive(false);
}
}
if (activePairings.isEmpty()) {
completeRound();
}
}
@Override
public boolean completeRound() {
if (activeRound < totalRounds) {
if (continualPairing) {
generateActivePairings();
}
return true;
} else {
endTournament();
return false;
}
}
@Override
public void endTournament() {
this.activePairings.clear();
}
public int getFurthestRound(int index) {
// If won in round 3, furthest round is 4
// If lost in any round, that's the furthest round
for (int i = completedPairings.size(); --i >= 0;) {
TournamentPairing pairing = completedPairings.get(i);
for(TournamentPlayer player : pairing.getPairedPlayers()) {
if (player.getIndex() == index) {
int roundAdjustment = pairing.getWinner().equals(player) ? 1 : 0;
return pairing.getRound() + roundAdjustment;
}
}
}
// Really shouldn't get here
return 0;
}
public static TournamentBracket importFromXML() {
TournamentBracket bracket = new TournamentBracket(3, 2);
return bracket;
}
}

View File

@@ -0,0 +1,60 @@
package forge.tournament.system;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import forge.LobbyPlayer;
import forge.game.GameOutcome;
import java.util.ArrayList;
import java.util.List;
public class TournamentPairing {
private int round;
private boolean bye = false;
private final List<TournamentPlayer> pairedPlayers = new ArrayList<TournamentPlayer>();
private final List<GameOutcome> outcomes = new ArrayList<GameOutcome>();
private TournamentPlayer winner;
public TournamentPairing(int rnd, List<TournamentPlayer> plyrs) {
pairedPlayers.addAll(plyrs);
round = rnd;
winner = null;
}
public int getRound() { return round; }
public void setRound(int round) { this.round = round; }
public boolean isBye() { return bye; }
public void setBye(boolean bye) { this.bye = bye; }
public List<TournamentPlayer> getPairedPlayers() { return pairedPlayers; }
public List<GameOutcome> getOutcomes() { return outcomes; }
public TournamentPlayer getWinner() { return winner; }
public void setWinner(TournamentPlayer winner) { this.winner = winner; }
public void setWinnerByIndex(int index) {
for(TournamentPlayer pl : pairedPlayers) {
if (pl.getIndex() == index) {
this.winner = pl;
return;
}
}
}
public boolean hasPlayer(LobbyPlayer player) {
for(TournamentPlayer pl : this.pairedPlayers) {
if (pl.getPlayer().equals(player)) {
return true;
}
}
return false;
}
public void exportToXML(HierarchicalStreamWriter writer) {
}
}

View File

@@ -0,0 +1,71 @@
package forge.tournament.system;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import forge.LobbyPlayer;
public class TournamentPlayer {
private LobbyPlayer player;
private int wins = 0,
losses = 0,
ties = 0;
private boolean active = true;
private int index = -1;
public TournamentPlayer(LobbyPlayer plyr) {
player = plyr;
}
public TournamentPlayer(LobbyPlayer plyr, int idx) {
player = plyr;
index = idx;
}
public LobbyPlayer getPlayer() {
return player;
}
public void setPlayer(LobbyPlayer player) {
this.player = player;
}
public int getWins() {
return wins;
}
public void setWins(int wins) {
this.wins = wins;
}
public int getLosses() {
return losses;
}
public void setLosses(int losses) {
this.losses = losses;
}
public int getTies() {
return ties;
}
public void setTies(int ties) {
this.ties = ties;
}
public boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
public int getIndex() { return index; }
public void setIndex(int index) { this.index = index; }
public void exportToXML(HierarchicalStreamWriter writer) {
}
}

View File

@@ -0,0 +1,33 @@
package forge.tournament.system;
import java.util.List;
public class TournamentRoundRobin extends AbstractTournament {
// Round Robin tournaments where you play everyone in your group/pod. Declare winner or break to top X
public TournamentRoundRobin(int ttlRnds, List<TournamentPlayer> allPlayers) {
super(ttlRnds, allPlayers);
initializeTournament();
}
@Override
public void generateActivePairings() {
}
@Override
public void reportMatchCompletion(TournamentPairing pairing) {
}
@Override
public boolean completeRound() {
return false;
}
@Override
public void endTournament() {
}
}

View File

@@ -0,0 +1,31 @@
package forge.tournament.system;
import java.util.List;
public class TournamentSwiss extends AbstractTournament {
public TournamentSwiss(int ttlRnds, List<TournamentPlayer> allPlayers) {
super(ttlRnds, allPlayers);
initializeTournament();
}
@Override
public void generateActivePairings() {
}
@Override
public void reportMatchCompletion(TournamentPairing pairing) {
}
@Override
public boolean completeRound() {
return false;
}
@Override
public void endTournament() {
}
}