mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 19:28:01 +00:00
- Initial checkin for Tournaments with a (currently disabled) flag to make use of it in Quest Draft
This commit is contained in:
10
.gitattributes
vendored
10
.gitattributes
vendored
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
148
forge-gui/src/main/java/forge/tournament/TournamentData.java
Normal file
148
forge-gui/src/main/java/forge/tournament/TournamentData.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
190
forge-gui/src/main/java/forge/tournament/TournamentIO.java
Normal file
190
forge-gui/src/main/java/forge/tournament/TournamentIO.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
76
forge-gui/src/main/java/forge/tournament/TournamentUtil.java
Normal file
76
forge-gui/src/main/java/forge/tournament/TournamentUtil.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user