mirror of
https://github.com/correl/mage.git
synced 2024-11-14 11:09:31 +00:00
New feature: "Chaos Remixed" booster draft (#10328)
* Fix error in draft pick logger that was failing on chaos drafts with fewer than 3 sets * Implement Remixed Booster Draft * Add debug test * minor cleanup * Cleanup unnecessary checks * Fix elimination tournament type * Add note for future improvement
This commit is contained in:
parent
6d4e353867
commit
4cc9329b15
20 changed files with 437 additions and 47 deletions
|
@ -55,6 +55,7 @@ public class NewTournamentDialog extends MageDialog {
|
|||
private static final int CONSTRUCTION_TIME_MAX = 30;
|
||||
private boolean isRandom = false;
|
||||
private boolean isRichMan = false;
|
||||
private boolean isRemixed = false;
|
||||
private String cubeFromDeckFilename = "";
|
||||
private String jumpstartPacksFilename = "";
|
||||
private boolean automaticChange = false;
|
||||
|
@ -679,11 +680,11 @@ public class NewTournamentDialog extends MageDialog {
|
|||
|
||||
// CHECKS
|
||||
TournamentTypeView tournamentType = (TournamentTypeView) cbTournamentType.getSelectedItem();
|
||||
if (tournamentType.isRandom() || tournamentType.isRichMan()) {
|
||||
if (tOptions.getLimitedOptions().getSetCodes().size() < tournamentType.getNumBoosters()) {
|
||||
if (tournamentType.isRandom() || tournamentType.isRichMan() || tournamentType.isRemixed()) {
|
||||
if (tOptions.getLimitedOptions().getSetCodes().size() < 1) {
|
||||
JOptionPane.showMessageDialog(
|
||||
MageFrame.getDesktop(),
|
||||
String.format("Warning, you must select %d packs for the pool", tournamentType.getNumBoosters()),
|
||||
"Warning, you must select at least one set for the pool",
|
||||
"Warning",
|
||||
JOptionPane.WARNING_MESSAGE
|
||||
);
|
||||
|
@ -916,7 +917,8 @@ public class NewTournamentDialog extends MageDialog {
|
|||
if (tournamentType.isLimited()) {
|
||||
this.isRandom = tournamentType.isRandom();
|
||||
this.isRichMan = tournamentType.isRichMan();
|
||||
if (this.isRandom || this.isRichMan) {
|
||||
this.isRemixed = tournamentType.isRemixed();
|
||||
if (this.isRandom || this.isRichMan || this.isRemixed) {
|
||||
createRandomPacks();
|
||||
} else {
|
||||
createPacks(tournamentType.getNumBoosters());
|
||||
|
@ -960,7 +962,7 @@ public class NewTournamentDialog extends MageDialog {
|
|||
this.lblPacks.setVisible(false);
|
||||
this.pnlPacks.setVisible(false);
|
||||
this.pnlRandomPacks.setVisible(false);
|
||||
} else if (tournamentType.isRandom() || tournamentType.isRichMan()) {
|
||||
} else if (tournamentType.isRandom() || tournamentType.isRichMan() || tournamentType.isRemixed()) {
|
||||
this.lblDraftCube.setVisible(false);
|
||||
this.cbDraftCube.setVisible(false);
|
||||
this.lblPacks.setVisible(true);
|
||||
|
@ -1031,7 +1033,7 @@ public class NewTournamentDialog extends MageDialog {
|
|||
pnlRandomPacks.add(txtRandomPacks);
|
||||
JButton btnSelectRandomPacks = new JButton();
|
||||
btnSelectRandomPacks.setAlignmentX(Component.LEFT_ALIGNMENT);
|
||||
btnSelectRandomPacks.setText("Select packs to be included in the pool");
|
||||
btnSelectRandomPacks.setText("Select sets to be included in the pool");
|
||||
btnSelectRandomPacks.setToolTipText(RandomPacksSelectorDialog.randomDraftDescription);
|
||||
btnSelectRandomPacks.addActionListener(evt -> showRandomPackSelectorDialog());
|
||||
pnlRandomPacks.add(btnSelectRandomPacks);
|
||||
|
@ -1044,8 +1046,7 @@ public class NewTournamentDialog extends MageDialog {
|
|||
}
|
||||
|
||||
private void showRandomPackSelectorDialog() {
|
||||
TournamentTypeView tournamentType = (TournamentTypeView) cbTournamentType.getSelectedItem();
|
||||
randomPackSelector.showDialog(isRandom, isRichMan, tournamentType.getNumBoosters());
|
||||
randomPackSelector.showDialog(isRandom, isRichMan, isRemixed);
|
||||
this.txtRandomPacks.setText(String.join(";", randomPackSelector.getSelectedPacks()));
|
||||
this.pack();
|
||||
this.revalidate();
|
||||
|
@ -1248,6 +1249,7 @@ public class NewTournamentDialog extends MageDialog {
|
|||
if (tournamentType.isLimited()) {
|
||||
tOptions.getLimitedOptions().setConstructionTime((Integer) this.spnConstructTime.getValue() * 60);
|
||||
tOptions.getLimitedOptions().setIsRandom(tournamentType.isRandom());
|
||||
tOptions.getLimitedOptions().setIsRemixed(tournamentType.isRemixed());
|
||||
tOptions.getLimitedOptions().setIsRichMan(tournamentType.isRichMan());
|
||||
tOptions.getLimitedOptions().setIsJumpstart(tournamentType.isJumpstart());
|
||||
|
||||
|
@ -1283,6 +1285,7 @@ public class NewTournamentDialog extends MageDialog {
|
|||
} else if (tournamentType.isRandom() || tournamentType.isRichMan()) {
|
||||
this.isRandom = tournamentType.isRandom();
|
||||
this.isRichMan = tournamentType.isRichMan();
|
||||
this.isRemixed = tournamentType.isRemixed();
|
||||
tOptions.getLimitedOptions().getSetCodes().clear();
|
||||
java.util.List<String> selected = randomPackSelector.getSelectedPacks();
|
||||
Collections.shuffle(selected);
|
||||
|
@ -1299,6 +1302,12 @@ public class NewTournamentDialog extends MageDialog {
|
|||
} else {
|
||||
tOptions.getLimitedOptions().getSetCodes().addAll(selected);
|
||||
}
|
||||
} else if (tournamentType.isRemixed()) {
|
||||
this.isRandom = tournamentType.isRandom();
|
||||
this.isRichMan = tournamentType.isRichMan();
|
||||
this.isRemixed = tournamentType.isRemixed();
|
||||
tOptions.getLimitedOptions().getSetCodes().clear();
|
||||
tOptions.getLimitedOptions().getSetCodes().addAll(randomPackSelector.getSelectedPacks());
|
||||
} else {
|
||||
for (JPanel panel : packPanels) {
|
||||
JComboBox combo = findComboInComponent(panel);
|
||||
|
@ -1383,7 +1392,7 @@ public class NewTournamentDialog extends MageDialog {
|
|||
numPlayers = Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TOURNAMENT_PLAYERS_DRAFT + versionStr, "4"));
|
||||
prepareTourneyView(numPlayers);
|
||||
|
||||
if (tournamentType.isRandom() || tournamentType.isRichMan()) {
|
||||
if (tournamentType.isRandom() || tournamentType.isRichMan() || tournamentType.isRemixed()) {
|
||||
loadRandomPacks(version);
|
||||
} else {
|
||||
loadBoosterPacks(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TOURNAMENT_PACKS_DRAFT + versionStr, ""));
|
||||
|
@ -1444,7 +1453,7 @@ public class NewTournamentDialog extends MageDialog {
|
|||
if (deckFile != null && !deckFile.isEmpty()) {
|
||||
PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_DECK_FILE + versionStr, deckFile);
|
||||
}
|
||||
if (tOptions.getLimitedOptions().getIsRandom() || tOptions.getLimitedOptions().getIsRichMan()) {
|
||||
if (tOptions.getLimitedOptions().getIsRandom() || tOptions.getLimitedOptions().getIsRichMan() || tOptions.getLimitedOptions().getIsRemixed()) {
|
||||
PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TOURNAMENT_PACKS_RANDOM_DRAFT + versionStr, String.join(";", this.randomPackSelector.getSelectedPacks()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ public class RandomPacksSelectorDialog extends javax.swing.JDialog {
|
|||
* Creates new form RandomPacksSelectorDialog
|
||||
*/
|
||||
private boolean boxesCreated;
|
||||
private int needSetsAmount;
|
||||
public static final String randomDraftDescription = ("The selected packs will be randomly distributed to players. Each player may open different packs. Duplicates will be avoided.");
|
||||
|
||||
public RandomPacksSelectorDialog() {
|
||||
|
@ -28,21 +27,22 @@ public class RandomPacksSelectorDialog extends javax.swing.JDialog {
|
|||
boxesCreated = false;
|
||||
}
|
||||
|
||||
private void setType(boolean isRandomDraft, boolean isRichManDraft, int needSetsAmount) {
|
||||
this.needSetsAmount = needSetsAmount;
|
||||
String title = "";
|
||||
private void setType(boolean isRandomDraft, boolean isRichManDraft, boolean isRemixedDraft) {
|
||||
String title;
|
||||
if (isRandomDraft) {
|
||||
title = "Random Booster Draft Packs Selector";
|
||||
} else if (isRichManDraft) {
|
||||
title = "Rich Man Booster Draft Packs Selector";
|
||||
} else if (isRemixedDraft) {
|
||||
title = "Chaos Remixed Draft Set Selector";
|
||||
} else {
|
||||
title = "Booster Draft Packs Selector";
|
||||
}
|
||||
setTitle(title);
|
||||
}
|
||||
|
||||
public void showDialog(boolean isRandomDraft, boolean isRichManDraft, int needSetsAmount) {
|
||||
setType(isRandomDraft, isRichManDraft, needSetsAmount);
|
||||
public void showDialog(boolean isRandomDraft, boolean isRichManDraft, boolean isRemixedDraft) {
|
||||
setType(isRandomDraft, isRichManDraft, isRemixedDraft);
|
||||
createCheckboxes();
|
||||
pnlPacks.setVisible(true);
|
||||
pnlPacks.revalidate();
|
||||
|
@ -204,8 +204,8 @@ public class RandomPacksSelectorDialog extends javax.swing.JDialog {
|
|||
}//GEN-LAST:event_formWindowClosing
|
||||
|
||||
public void doApply() {
|
||||
if (getSelectedPacks().size() < needSetsAmount) {
|
||||
JOptionPane.showMessageDialog(this, String.format("At least %d sets must be selected", needSetsAmount), "Error", JOptionPane.ERROR_MESSAGE);
|
||||
if (getSelectedPacks().size() < 1) {
|
||||
JOptionPane.showMessageDialog(this, "At least one set must be selected", "Error", JOptionPane.ERROR_MESSAGE);
|
||||
} else {
|
||||
this.setVisible(false);
|
||||
}
|
||||
|
|
|
@ -148,7 +148,7 @@
|
|||
|
||||
public void updateDraft(DraftView draftView) {
|
||||
if (draftView.getSets().size() != 3) {
|
||||
// Random draft
|
||||
// Random draft - TODO: can we access the type of draft here?
|
||||
this.txtPack1.setText("Random Boosters");
|
||||
this.txtPack2.setText("Random Boosters");
|
||||
this.txtPack3.setText("Random Boosters");
|
||||
|
@ -171,6 +171,7 @@
|
|||
int left = draftView.getPlayers().size() - right;
|
||||
int height = left * 18;
|
||||
lblTableImage.setSize(new Dimension(lblTableImage.getWidth(), height));
|
||||
// TODO: Can we fix this for Rich Draft where there is no direction?
|
||||
Image tableImage = ImageHelper.getImageFromResources(draftView.getBoosterNum() % 2 == 1 ? "/draft/table_left.png" : "/draft/table_right.png");
|
||||
BufferedImage resizedTable = ImageHelper.getResizedImage(BufferedImageBuilder.bufferImage(tableImage, BufferedImage.TYPE_INT_ARGB), lblTableImage.getWidth(), lblTableImage.getHeight());
|
||||
lblTableImage.setIcon(new ImageIcon(resizedTable));
|
||||
|
@ -431,10 +432,11 @@
|
|||
}
|
||||
|
||||
private String getCurrentSetCode() {
|
||||
if (!setCodes.isEmpty()) {
|
||||
// TODO: Record set codes for random drafts correctly
|
||||
if (setCodes.size() >= packNo) {
|
||||
return setCodes.get(packNo - 1);
|
||||
} else {
|
||||
return "";
|
||||
return " ";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ public class TournamentTypeView implements Serializable {
|
|||
private final boolean cubeBooster;
|
||||
private final boolean elimination;
|
||||
private final boolean random;
|
||||
private final boolean remixed;
|
||||
private final boolean richMan;
|
||||
private final boolean jumpstart;
|
||||
|
||||
|
@ -34,6 +35,7 @@ public class TournamentTypeView implements Serializable {
|
|||
this.cubeBooster = tournamentType.isCubeBooster();
|
||||
this.elimination = tournamentType.isElimination();
|
||||
this.random = tournamentType.isRandom();
|
||||
this.remixed = tournamentType.isRemixed();
|
||||
this.richMan = tournamentType.isRichMan();
|
||||
this.jumpstart = tournamentType.isJumpstart();
|
||||
}
|
||||
|
@ -79,6 +81,10 @@ public class TournamentTypeView implements Serializable {
|
|||
return random;
|
||||
}
|
||||
|
||||
public boolean isRemixed() {
|
||||
return remixed;
|
||||
}
|
||||
|
||||
public boolean isRichMan() {
|
||||
return richMan;
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ public class RandomBoosterDraftEliminationTournamentType extends TournamentType
|
|||
this.draft = true;
|
||||
this.limited = true;
|
||||
this.cubeBooster = false;
|
||||
this.elimination = false;
|
||||
this.elimination = true;
|
||||
this.isRandom = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
package mage.tournament;
|
||||
|
||||
import mage.constants.TournamentPlayerState;
|
||||
import mage.game.draft.DraftOptions;
|
||||
import mage.game.draft.RemixedBoosterDraft;
|
||||
import mage.game.events.TableEvent;
|
||||
import mage.game.tournament.TournamentOptions;
|
||||
import mage.game.tournament.TournamentPlayer;
|
||||
|
||||
public class RemixedBoosterDraftEliminationTournament extends BoosterDraftEliminationTournament {
|
||||
|
||||
public RemixedBoosterDraftEliminationTournament(TournamentOptions options) {
|
||||
super(options);
|
||||
currentStep = TournamentStep.START;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void draft() {
|
||||
draft = new RemixedBoosterDraft((DraftOptions) options.getLimitedOptions(), getSets());
|
||||
for (TournamentPlayer player: players.values()) {
|
||||
draft.addPlayer(player.getPlayer());
|
||||
player.setState(TournamentPlayerState.DRAFTING);
|
||||
}
|
||||
tableEventSource.fireTableEvent(TableEvent.EventType.START_DRAFT, null, draft);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
|
||||
|
||||
package mage.tournament;
|
||||
|
||||
import mage.game.tournament.TournamentType;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public class RemixedBoosterDraftEliminationTournamentType extends TournamentType {
|
||||
|
||||
public RemixedBoosterDraftEliminationTournamentType() {
|
||||
this.name = "Booster Draft Elimination (Remixed)";
|
||||
this.maxPlayers = 16;
|
||||
this.minPlayers = 4;
|
||||
this.numBoosters = 3;
|
||||
this.draft = true;
|
||||
this.limited = true;
|
||||
this.cubeBooster = false;
|
||||
this.elimination = true;
|
||||
this.isRemixed = true;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package mage.tournament;
|
||||
|
||||
import mage.constants.TournamentPlayerState;
|
||||
import mage.game.draft.DraftOptions;
|
||||
import mage.game.draft.RemixedBoosterDraft;
|
||||
import mage.game.events.TableEvent;
|
||||
import mage.game.tournament.TournamentOptions;
|
||||
import mage.game.tournament.TournamentPlayer;
|
||||
|
||||
public class RemixedBoosterDraftSwissTournament extends BoosterDraftSwissTournament {
|
||||
|
||||
public RemixedBoosterDraftSwissTournament(TournamentOptions options) {
|
||||
super(options);
|
||||
currentStep = BoosterDraftSwissTournament.TournamentStep.START;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void draft() {
|
||||
draft = new RemixedBoosterDraft((DraftOptions) options.getLimitedOptions(), getSets());
|
||||
for (TournamentPlayer player: players.values()) {
|
||||
draft.addPlayer(player.getPlayer());
|
||||
player.setState(TournamentPlayerState.DRAFTING);
|
||||
}
|
||||
tableEventSource.fireTableEvent(TableEvent.EventType.START_DRAFT, null, draft);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
|
||||
|
||||
package mage.tournament;
|
||||
|
||||
import mage.game.tournament.TournamentType;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class RemixedBoosterDraftSwissTournamentType extends TournamentType {
|
||||
|
||||
public RemixedBoosterDraftSwissTournamentType() {
|
||||
this.name = "Booster Draft Swiss (Remixed)";
|
||||
this.maxPlayers = 16;
|
||||
this.minPlayers = 4;
|
||||
this.numBoosters = 3;
|
||||
this.draft = true;
|
||||
this.limited = true;
|
||||
this.cubeBooster = false;
|
||||
this.elimination = false;
|
||||
this.isRemixed = true;
|
||||
}
|
||||
|
||||
}
|
|
@ -95,13 +95,13 @@
|
|||
<tournamentType name="Booster Draft Elimination" jar="mage-tournament-booster-draft.jar" className="mage.tournament.BoosterDraftEliminationTournament" typeName="mage.tournament.BoosterDraftEliminationTournamentType"/>
|
||||
<tournamentType name="Booster Draft Elimination (Cube)" jar="mage-tournament-booster-draft.jar" className="mage.tournament.BoosterDraftEliminationTournament" typeName="mage.tournament.BoosterDraftEliminationCubeTournamentType"/>
|
||||
<tournamentType name="Booster Draft Elimination (Random)" jar="mage-tournament-booster-draft.jar" className="mage.tournament.RandomBoosterDraftEliminationTournament" typeName="mage.tournament.RandomBoosterDraftEliminationTournamentType"/>
|
||||
|
||||
<tournamentType name="Booster Draft Elimination (Remixed)" jar="mage-tournament-booster-draft.jar" className="mage.tournament.RemixedBoosterDraftEliminationTournament" typeName="mage.tournament.RemixedBoosterDraftEliminationTournamentType"/>
|
||||
<tournamentType name="Booster Draft Elimination (Rich Man)" jar="mage-tournament-booster-draft.jar" className="mage.tournament.RichManDraftEliminationTournament" typeName="mage.tournament.RichManDraftEliminationTournamentType"/>
|
||||
|
||||
<tournamentType name="Booster Draft Elimination (Rich Man Cube)" jar="mage-tournament-booster-draft.jar" className="mage.tournament.RichManCubeDraftEliminationTournament" typeName="mage.tournament.RichManCubeDraftEliminationTournamentType"/>
|
||||
<tournamentType name="Booster Draft Swiss" jar="mage-tournament-booster-draft.jar" className="mage.tournament.BoosterDraftSwissTournament" typeName="mage.tournament.BoosterDraftSwissTournamentType"/>
|
||||
<tournamentType name="Booster Draft Swiss (Cube)" jar="mage-tournament-booster-draft.jar" className="mage.tournament.BoosterDraftSwissTournament" typeName="mage.tournament.BoosterDraftSwissCubeTournamentType"/>
|
||||
<tournamentType name="Booster Draft Swiss (Random)" jar="mage-tournament-booster-draft.jar" className="mage.tournament.RandomBoosterDraftSwissTournament" typeName="mage.tournament.RandomBoosterDraftSwissTournamentType"/>
|
||||
<tournamentType name="Booster Draft Swiss (Remixed)" jar="mage-tournament-booster-draft.jar" className="mage.tournament.RemixedBoosterDraftSwissTournament" typeName="mage.tournament.RemixedBoosterDraftSwissTournamentType"/>
|
||||
<tournamentType name="Sealed Elimination" jar="mage-tournament-sealed.jar" className="mage.tournament.SealedEliminationTournament" typeName="mage.tournament.SealedEliminationTournamentType"/>
|
||||
<tournamentType name="Sealed Elimination (Cube)" jar="mage-tournament-sealed.jar" className="mage.tournament.SealedEliminationTournament" typeName="mage.tournament.SealedEliminationCubeTournamentType"/>
|
||||
<tournamentType name="Sealed Swiss" jar="mage-tournament-sealed.jar" className="mage.tournament.SealedSwissTournament" typeName="mage.tournament.SealedSwissTournamentType"/>
|
||||
|
|
|
@ -89,11 +89,13 @@
|
|||
<tournamentType name="Booster Draft Elimination" jar="mage-tournament-boosterdraft-${project.version}.jar" className="mage.tournament.BoosterDraftEliminationTournament" typeName="mage.tournament.BoosterDraftEliminationTournamentType"/>
|
||||
<tournamentType name="Booster Draft Elimination (Cube)" jar="mage-tournament-boosterdraft-${project.version}.jar" className="mage.tournament.BoosterDraftEliminationTournament" typeName="mage.tournament.BoosterDraftEliminationCubeTournamentType"/>
|
||||
<tournamentType name="Booster Draft Elimination (Random)" jar="mage-tournament-boosterdraft-${project.version}.jar" className="mage.tournament.RandomBoosterDraftEliminationTournament" typeName="mage.tournament.RandomBoosterDraftEliminationTournamentType"/>
|
||||
<tournamentType name="Booster Draft Elimination (Remixed)" jar="mage-tournament-boosterdraft-${project.version}.jar" className="mage.tournament.RemixedBoosterDraftEliminationTournament" typeName="mage.tournament.RemixedBoosterDraftEliminationTournamentType"/>
|
||||
<tournamentType name="Booster Draft Elimination (Rich Man)" jar="mage-tournament-boosterdraft-${project.version}.jar" className="mage.tournament.RichManDraftEliminationTournament" typeName="mage.tournament.RichManDraftEliminationTournamentType"/>
|
||||
<tournamentType name="Booster Draft Elimination (Rich Man Cube)" jar="mage-tournament-boosterdraft-${project.version}.jar" className="mage.tournament.RichManCubeDraftEliminationTournament" typeName="mage.tournament.RichManCubeDraftEliminationTournamentType"/>
|
||||
<tournamentType name="Booster Draft Swiss" jar="mage-tournament-boosterdraft-${project.version}.jar" className="mage.tournament.BoosterDraftSwissTournament" typeName="mage.tournament.BoosterDraftSwissTournamentType"/>
|
||||
<tournamentType name="Booster Draft Swiss (Cube)" jar="mage-tournament-boosterdraft-${project.version}.jar" className="mage.tournament.BoosterDraftSwissTournament" typeName="mage.tournament.BoosterDraftSwissCubeTournamentType"/>
|
||||
<tournamentType name="Booster Draft Swiss (Random)" jar="mage-tournament-boosterdraft-${project.version}.jar" className="mage.tournament.RandomBoosterDraftSwissTournament" typeName="mage.tournament.RandomBoosterDraftSwissTournamentType"/>
|
||||
<tournamentType name="Booster Draft Swiss (Remixed)" jar="mage-tournament-boosterdraft-${project.version}.jar" className="mage.tournament.RemixedBoosterDraftSwissTournament" typeName="mage.tournament.RemixedBoosterDraftSwissTournamentType"/>
|
||||
<tournamentType name="Sealed Elimination" jar="mage-tournament-sealed-${project.version}.jar" className="mage.tournament.SealedEliminationTournament" typeName="mage.tournament.SealedEliminationTournamentType"/>
|
||||
<tournamentType name="Sealed Elimination (Cube)" jar="mage-tournament-sealed-${project.version}.jar" className="mage.tournament.SealedEliminationTournament" typeName="mage.tournament.SealedEliminationCubeTournamentType"/>
|
||||
<tournamentType name="Sealed Swiss" jar="mage-tournament-sealed-${project.version}.jar" className="mage.tournament.SealedSwissTournament" typeName="mage.tournament.SealedSwissTournamentType"/>
|
||||
|
|
|
@ -55,7 +55,14 @@ public enum TournamentFactory {
|
|||
tournament.getOptions().getLimitedOptions().setDraftCube(draftCube);
|
||||
tournament.setBoosterInfo(tournament.getOptions().getLimitedOptions().getDraftCubeName());
|
||||
} else if (tournament.getTournamentType().isRandom()) {
|
||||
StringBuilder rv = new StringBuilder( "Random Draft using sets: ");
|
||||
StringBuilder rv = new StringBuilder( "Chaos Draft using sets: ");
|
||||
for (Map.Entry<String, Integer> entry: setInfo.entrySet()){
|
||||
rv.append(entry.getKey());
|
||||
rv.append(';');
|
||||
}
|
||||
tournament.setBoosterInfo(rv.toString());
|
||||
} else if (tournament.getTournamentType().isRemixed()) {
|
||||
StringBuilder rv = new StringBuilder( "Chaos Remixed Draft using sets: ");
|
||||
for (Map.Entry<String, Integer> entry: setInfo.entrySet()){
|
||||
rv.append(entry.getKey());
|
||||
rv.append(';');
|
||||
|
@ -94,4 +101,4 @@ public enum TournamentFactory {
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,11 +60,13 @@
|
|||
<tournamentType name="Booster Draft Elimination" jar="mage-tournament-booster-draft.jar" className="mage.tournament.BoosterDraftEliminationTournament" typeName="mage.tournament.BoosterDraftEliminationTournamentType"/>
|
||||
<tournamentType name="Booster Draft Elimination (Cube)" jar="mage-tournament-booster-draft.jar" className="mage.tournament.BoosterDraftEliminationTournament" typeName="mage.tournament.BoosterDraftEliminationCubeTournamentType"/>
|
||||
<tournamentType name="Booster Draft Elimination (Random)" jar="mage-tournament-booster-draft.jar" className="mage.tournament.RandomBoosterDraftEliminationTournament" typeName="mage.tournament.RandomBoosterDraftEliminationTournamentType"/>
|
||||
<tournamentType name="Booster Draft Elimination (Remixed)" jar="mage-tournament-booster-draft.jar" className="mage.tournament.RemixedBoosterDraftEliminationTournament" typeName="mage.tournament.RemixedBoosterDraftEliminationTournamentType"/>
|
||||
<tournamentType name="Booster Draft Elimination (Rich Man)" jar="mage-tournament-booster-draft.jar" className="mage.tournament.RichManDraftEliminationTournament" typeName="mage.tournament.RichManDraftEliminationTournamentType"/>
|
||||
<tournamentType name="Booster Draft Elimination (Rich Man Cube)" jar="mage-tournament-booster-draft.jar" className="mage.tournament.RichManCubeDraftEliminationTournament" typeName="mage.tournament.RichManCubeDraftEliminationTournamentType"/>
|
||||
<tournamentType name="Booster Draft Swiss" jar="mage-tournament-booster-draft.jar" className="mage.tournament.BoosterDraftSwissTournament" typeName="mage.tournament.BoosterDraftSwissTournamentType"/>
|
||||
<tournamentType name="Booster Draft Swiss (Cube)" jar="mage-tournament-booster-draft.jar" className="mage.tournament.BoosterDraftSwissTournament" typeName="mage.tournament.BoosterDraftSwissCubeTournamentType"/>
|
||||
<tournamentType name="Booster Draft Swiss (Random)" jar="mage-tournament-booster-draft.jar" className="mage.tournament.RandomBoosterDraftSwissTournament" typeName="mage.tournament.RandomBoosterDraftSwissTournamentType"/>
|
||||
<tournamentType name="Booster Draft Swiss (Remixed)" jar="mage-tournament-booster-draft.jar" className="mage.tournament.RemixedBoosterDraftSwissTournament" typeName="mage.tournament.RemixedBoosterDraftSwissTournamentType"/>
|
||||
<tournamentType name="Sealed Elimination" jar="mage-tournament-sealed.jar" className="mage.tournament.SealedEliminationTournament" typeName="mage.tournament.SealedEliminationTournamentType"/>
|
||||
<tournamentType name="Sealed Elimination (Cube)" jar="mage-tournament-sealed.jar" className="mage.tournament.SealedEliminationTournament" typeName="mage.tournament.SealedEliminationCubeTournamentType"/>
|
||||
<tournamentType name="Sealed Swiss" jar="mage-tournament-sealed.jar" className="mage.tournament.SealedSwissTournament" typeName="mage.tournament.SealedSwissTournamentType"/>
|
||||
|
|
|
@ -11,6 +11,7 @@ import mage.cards.repository.CardScanner;
|
|||
import mage.constants.CardType;
|
||||
import mage.constants.Rarity;
|
||||
import mage.constants.SubType;
|
||||
import mage.game.draft.RemixedSet;
|
||||
import mage.sets.*;
|
||||
import mage.util.CardUtil;
|
||||
import org.junit.Assert;
|
||||
|
@ -552,7 +553,7 @@ public class BoosterGenerationTest extends MageTestBase {
|
|||
@Test
|
||||
public void test_CollectBoosterStats() {
|
||||
ExpansionSet setToAnalyse = FallenEmpires.getInstance();
|
||||
int openBoosters = 1000;
|
||||
int openBoosters = 10000;
|
||||
|
||||
Map<String, Integer> resRatio = new HashMap<>();
|
||||
int totalCards = 0;
|
||||
|
@ -565,21 +566,47 @@ public class BoosterGenerationTest extends MageTestBase {
|
|||
resRatio.computeIfPresent(code, (u, count) -> count + 1);
|
||||
});
|
||||
}
|
||||
final Integer totalCardsFinal = totalCards;
|
||||
List<String> info = resRatio.entrySet().stream()
|
||||
.sorted(new Comparator<Map.Entry<String, Integer>>() {
|
||||
@Override
|
||||
public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
|
||||
return Integer.compare(o2.getValue(), o1.getValue());
|
||||
}
|
||||
})
|
||||
.sorted((o1, o2) -> Integer.compare(o2.getValue(), o1.getValue()))
|
||||
.map(e -> String.format("%s: %d",
|
||||
e.getKey(),
|
||||
e.getValue()
|
||||
//(double) e.getValue() / totalCardsFinal * 100.0
|
||||
))
|
||||
.collect(Collectors.toList());
|
||||
System.out.println(setToAnalyse.getName() + " - boosters opened: " + openBoosters + ". Found cards: " + totalCardsFinal + "\n"
|
||||
+ info.stream().collect(Collectors.joining("\n")));
|
||||
System.out.println(setToAnalyse.getName() + " - boosters opened: " + openBoosters + ". Found cards: " + totalCards + "\n"
|
||||
+ String.join("\n", info));
|
||||
}
|
||||
|
||||
@Ignore // debug only
|
||||
@Test
|
||||
public void test_RemixedBoosterStats() {
|
||||
List<ExpansionSet> sets = new ArrayList<>();
|
||||
sets.add(ScarsOfMirrodin.getInstance());
|
||||
sets.add(MirrodinBesieged.getInstance());
|
||||
sets.add(NewPhyrexia.getInstance());
|
||||
RemixedSet setToAnalyse = new RemixedSet(sets, 10, 3, 1);
|
||||
int openBoosters = 10000;
|
||||
|
||||
Map<String, Integer> resRatio = new HashMap<>();
|
||||
int totalCards = 0;
|
||||
for (int i = 1; i <= openBoosters; i++) {
|
||||
List<Card> booster = setToAnalyse.createBooster();
|
||||
totalCards += booster.size();
|
||||
booster.forEach(card -> {
|
||||
String code = String.format("%s %s", card.getRarity().getCode(), card.getName());
|
||||
resRatio.putIfAbsent(code, 0);
|
||||
resRatio.computeIfPresent(code, (u, count) -> count + 1);
|
||||
});
|
||||
}
|
||||
List<String> info = resRatio.entrySet().stream()
|
||||
.sorted((o1, o2) -> Integer.compare(o2.getValue(), o1.getValue()))
|
||||
.map(e -> String.format("%s: %d",
|
||||
e.getKey(),
|
||||
e.getValue()
|
||||
))
|
||||
.collect(Collectors.toList());
|
||||
System.out.println("Boosters opened: " + openBoosters + ". Found cards: " + totalCards + "\n"
|
||||
+ String.join("\n", info));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,7 +18,9 @@ public class ObjectColor implements Serializable, Copyable<ObjectColor>, Compara
|
|||
public static final ObjectColor RED = new ObjectColor("R");
|
||||
public static final ObjectColor GREEN = new ObjectColor("G");
|
||||
|
||||
public static final ObjectColor GOLD = new ObjectColor("O");
|
||||
public static final ObjectColor COLORLESS = new ObjectColor();
|
||||
|
||||
public static final ObjectColor GOLD = new ObjectColor("O"); // Not multicolored - Sword of Dungeons & Dragons
|
||||
|
||||
private boolean white;
|
||||
private boolean blue;
|
||||
|
|
|
@ -305,7 +305,7 @@ public abstract class ExpansionSet implements Serializable {
|
|||
return true;
|
||||
}
|
||||
|
||||
private static ObjectColor getColorForValidate(Card card) {
|
||||
public static ObjectColor getColorForValidate(Card card) {
|
||||
ObjectColor color = card.getColor();
|
||||
// treat colorless nonland cards with exactly one ID color as cards of that color
|
||||
// (e.g. devoid, emerge, spellbombs... but not mana fixing artifacts)
|
||||
|
@ -364,8 +364,6 @@ public abstract class ExpansionSet implements Serializable {
|
|||
return (RandomUtil.nextDouble() > Math.pow(0.8, colorlessCountPlusOne));
|
||||
}
|
||||
|
||||
private static final ObjectColor COLORLESS = new ObjectColor();
|
||||
|
||||
protected boolean validateUncommonColors(List<Card> booster) {
|
||||
List<ObjectColor> uncommonColors = booster.stream()
|
||||
.filter(card -> card.getRarity() == Rarity.UNCOMMON)
|
||||
|
@ -375,7 +373,7 @@ public abstract class ExpansionSet implements Serializable {
|
|||
// if there are only two uncommons, they can be the same color
|
||||
if (uncommonColors.size() < 3) return true;
|
||||
// boosters of artifact sets can have all colorless uncommons
|
||||
if (uncommonColors.contains(COLORLESS)) return true;
|
||||
if (uncommonColors.contains(ObjectColor.COLORLESS)) return true;
|
||||
// otherwise, reject if all uncommons are the same color combination
|
||||
return (new HashSet<>(uncommonColors).size() > 1);
|
||||
}
|
||||
|
|
28
Mage/src/main/java/mage/game/draft/RemixedBoosterDraft.java
Normal file
28
Mage/src/main/java/mage/game/draft/RemixedBoosterDraft.java
Normal file
|
@ -0,0 +1,28 @@
|
|||
package mage.game.draft;
|
||||
|
||||
import mage.cards.ExpansionSet;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class RemixedBoosterDraft extends BoosterDraft {
|
||||
|
||||
final RemixedSet remixedSet;
|
||||
|
||||
public RemixedBoosterDraft(DraftOptions options, List<ExpansionSet> sets) {
|
||||
super(options, sets);
|
||||
if (sets.isEmpty()){
|
||||
throw new RuntimeException("At least one set must be selected for remixed booster draft");
|
||||
}
|
||||
remixedSet = new RemixedSet(sets, 10, 3, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void openBooster() {
|
||||
if (boosterNum <= numberBoosters) {
|
||||
for (DraftPlayer player: players.values()) {
|
||||
player.setBooster(remixedSet.createBooster());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
191
Mage/src/main/java/mage/game/draft/RemixedSet.java
Normal file
191
Mage/src/main/java/mage/game/draft/RemixedSet.java
Normal file
|
@ -0,0 +1,191 @@
|
|||
package mage.game.draft;
|
||||
|
||||
import mage.ObjectColor;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.ExpansionSet;
|
||||
import mage.cards.repository.CardCriteria;
|
||||
import mage.cards.repository.CardInfo;
|
||||
import mage.cards.repository.CardRepository;
|
||||
import mage.constants.Rarity;
|
||||
import mage.util.RandomUtil;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class RemixedSet implements Serializable {
|
||||
|
||||
protected final int numBoosterCommons;
|
||||
protected final int numBoosterUncommons;
|
||||
protected final int numBoosterRares;
|
||||
protected final int numBoosterSpecials;
|
||||
protected final List<CardInfo> commons;
|
||||
protected final List<CardInfo> uncommons;
|
||||
protected final List<CardInfo> rares;
|
||||
protected final List<CardInfo> mythics;
|
||||
protected final List<CardInfo> specials;
|
||||
protected final double chanceMythic;
|
||||
|
||||
public RemixedSet (List<ExpansionSet> sets, int c, int u, int r) {
|
||||
this(sets, c, u, r, 0);
|
||||
}
|
||||
|
||||
public RemixedSet(List<ExpansionSet> sets, int c, int u, int r, int special) {
|
||||
this.numBoosterCommons = c;
|
||||
this.numBoosterUncommons = u;
|
||||
this.numBoosterRares = r;
|
||||
this.numBoosterSpecials = special; // TODO: add support for uploading a custom list of special cards
|
||||
this.commons = new ArrayList<>();
|
||||
this.uncommons = new ArrayList<>();
|
||||
this.rares = new ArrayList<>();
|
||||
this.mythics = new ArrayList<>();
|
||||
this.specials = new ArrayList<>();
|
||||
for (ExpansionSet set : sets) {
|
||||
commons.addAll(findCardsBySetAndRarity(set, Rarity.COMMON));
|
||||
uncommons.addAll(findCardsBySetAndRarity(set, Rarity.UNCOMMON));
|
||||
rares.addAll(findCardsBySetAndRarity(set, Rarity.RARE));
|
||||
mythics.addAll(findCardsBySetAndRarity(set, Rarity.MYTHIC));
|
||||
}
|
||||
float nMythics = mythics.size();
|
||||
float nRares = rares.size();
|
||||
this.chanceMythic = (nMythics / (nMythics + nRares + nRares));
|
||||
}
|
||||
|
||||
protected List<CardInfo> findCardsBySetAndRarity(ExpansionSet set, Rarity rarity) {
|
||||
List<CardInfo> cardInfos = CardRepository.instance.findCards(new CardCriteria()
|
||||
.setCodes(set.getCode())
|
||||
.rarities(rarity)
|
||||
.maxCardNumber(set.getMaxCardNumberInBooster())); // TODO: Make sure this parameter is set appropriately where needed
|
||||
|
||||
cardInfos.removeIf(next -> (
|
||||
next.getCardNumber().contains("*")
|
||||
|| next.getCardNumber().contains("+")));
|
||||
|
||||
return cardInfos;
|
||||
}
|
||||
|
||||
public List<Card> createBooster() {
|
||||
List<Card> booster = new ArrayList<>();
|
||||
booster.addAll(generateCommons());
|
||||
booster.addAll(generateUncommons());
|
||||
booster.addAll(generateRares());
|
||||
// TODO: Generate special cards
|
||||
return booster;
|
||||
}
|
||||
|
||||
protected void addToBooster(List<Card> booster, List<CardInfo> cards) {
|
||||
if (cards.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
CardInfo cardInfo = cards.remove(RandomUtil.nextInt(cards.size())); // so no duplicates in a booster
|
||||
Card card = cardInfo.getCard();
|
||||
if (card == null) {
|
||||
return;
|
||||
}
|
||||
booster.add(card);
|
||||
}
|
||||
|
||||
protected List<Card> generateCommons() {
|
||||
List<Card> boosterCommons = new ArrayList<>(); // will be returned once valid or max attempts reached
|
||||
for (int i = 0; i < 100; i++) { // don't want to somehow loop forever
|
||||
boosterCommons.clear();
|
||||
List<CardInfo> commonsForGenerate = new ArrayList<>(commons); // to not modify base list
|
||||
for (int j = 0; j < numBoosterCommons; j++) {
|
||||
addToBooster(boosterCommons, commonsForGenerate);
|
||||
}
|
||||
if (validateCommonColors(boosterCommons)) {
|
||||
return boosterCommons;
|
||||
}
|
||||
}
|
||||
return boosterCommons;
|
||||
}
|
||||
|
||||
protected List<Card> generateUncommons() {
|
||||
List<Card> boosterUncommons = new ArrayList<>(); // will be returned once valid or max attempts reached
|
||||
for (int i = 0; i < 100; i++) { // don't want to somehow loop forever
|
||||
boosterUncommons.clear();
|
||||
List<CardInfo> uncommonsForGenerate = new ArrayList<>(uncommons); // to not modify base list
|
||||
for (int j = 0; j < numBoosterUncommons; j++) {
|
||||
addToBooster(boosterUncommons, uncommonsForGenerate);
|
||||
}
|
||||
if (validateUncommonColors(boosterUncommons)) {
|
||||
return boosterUncommons;
|
||||
}
|
||||
}
|
||||
return boosterUncommons;
|
||||
}
|
||||
|
||||
protected List<Card> generateRares() {
|
||||
List<Card> boosterRares = new ArrayList<>();
|
||||
List<CardInfo> raresForGenerate = new ArrayList<>(rares);
|
||||
List<CardInfo> mythicsForGenerate = new ArrayList<>(mythics);
|
||||
for (int j = 0; j < numBoosterRares; j++) {
|
||||
if (RandomUtil.nextDouble() < chanceMythic) {
|
||||
addToBooster(boosterRares, mythicsForGenerate);
|
||||
} else {
|
||||
addToBooster(boosterRares, raresForGenerate);
|
||||
}
|
||||
}
|
||||
return boosterRares;
|
||||
}
|
||||
|
||||
// See ExpansionSet for original validation logic by awjackson
|
||||
protected boolean validateCommonColors(List<Card> booster) {
|
||||
List<ObjectColor> commonColors = booster.stream()
|
||||
.filter(card -> card.getRarity() == Rarity.COMMON)
|
||||
.map(ExpansionSet::getColorForValidate)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// for multicolor sets, count not just the colors present at common,
|
||||
// but also the number of color combinations (guilds/shards/wedges)
|
||||
// e.g. a booster with three UB commons, three RW commons and four G commons
|
||||
// has all five colors but isn't "balanced"
|
||||
ObjectColor colorsRepresented = new ObjectColor();
|
||||
Set<ObjectColor> colorCombinations = new HashSet<>();
|
||||
int colorlessCountPlusOne = 1;
|
||||
|
||||
for (ObjectColor color : commonColors) {
|
||||
colorCombinations.add(color);
|
||||
int colorCount = color.getColorCount();
|
||||
if (colorCount == 0) {
|
||||
++colorlessCountPlusOne;
|
||||
} else if (colorCount > 1) {
|
||||
// to prevent biasing toward multicolor over monocolor cards,
|
||||
// count them as one of their colors chosen at random
|
||||
List<ObjectColor> multiColor = color.getColors();
|
||||
colorsRepresented.addColor(multiColor.get(RandomUtil.nextInt(multiColor.size())));
|
||||
} else {
|
||||
colorsRepresented.addColor(color);
|
||||
}
|
||||
}
|
||||
|
||||
int colors = Math.min(colorsRepresented.getColorCount(), colorCombinations.size());
|
||||
|
||||
// if booster has all five colors in five unique combinations, or if it has
|
||||
// one card per color and all but one of the rest are colorless, accept it
|
||||
// ("all but one" adds some leeway for sets with small boosters)
|
||||
if (colors >= Math.min(5, commonColors.size() - colorlessCountPlusOne)) return true;
|
||||
// otherwise, if booster is missing more than one color, reject it
|
||||
if (colors < 4) return false;
|
||||
// otherwise, stochastically treat each colorless card as 1/5 of a card of the missing color
|
||||
return (RandomUtil.nextDouble() > Math.pow(0.8, colorlessCountPlusOne));
|
||||
}
|
||||
|
||||
protected boolean validateUncommonColors(List<Card> booster) {
|
||||
List<ObjectColor> uncommonColors = booster.stream()
|
||||
.filter(card -> card.getRarity() == Rarity.UNCOMMON)
|
||||
.map(ExpansionSet::getColorForValidate)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// if there are only two uncommons, they can be the same color
|
||||
if (uncommonColors.size() < 3) return true;
|
||||
// boosters of artifact sets can have all colorless uncommons
|
||||
if (uncommonColors.contains(ObjectColor.COLORLESS)) return true;
|
||||
// otherwise, reject if all uncommons are the same color combination
|
||||
return (new HashSet<>(uncommonColors).size() > 1);
|
||||
}
|
||||
|
||||
}
|
|
@ -17,6 +17,7 @@ public class LimitedOptions implements Serializable {
|
|||
protected DraftCube draftCube;
|
||||
protected int numberBoosters;
|
||||
protected boolean isRandom;
|
||||
protected boolean isRemixed;
|
||||
protected boolean isRichMan;
|
||||
protected Deck cubeFromDeck;
|
||||
|
||||
|
@ -95,6 +96,14 @@ public class LimitedOptions implements Serializable {
|
|||
this.isRandom = isRandom;
|
||||
}
|
||||
|
||||
public boolean getIsRemixed() {
|
||||
return isRemixed;
|
||||
}
|
||||
|
||||
public void setIsRemixed(boolean isRemixed) {
|
||||
this.isRemixed = isRemixed;
|
||||
}
|
||||
|
||||
public boolean getIsRichMan() {
|
||||
return isRichMan;
|
||||
}
|
||||
|
|
|
@ -14,11 +14,12 @@ public class TournamentType implements Serializable {
|
|||
protected int maxPlayers;
|
||||
protected int numBoosters;
|
||||
protected boolean cubeBooster; // boosters are generated from a defined cube
|
||||
protected boolean draft; // or sealed
|
||||
protected boolean limited; // or construced
|
||||
protected boolean elimination; // or Swiss
|
||||
protected boolean isRandom;
|
||||
protected boolean isRichMan; // or Rich Man Draft
|
||||
protected boolean draft; // else Sealed
|
||||
protected boolean limited; // else Constructed
|
||||
protected boolean elimination; // else Swiss
|
||||
protected boolean isRandom; // chaos draft
|
||||
protected boolean isRemixed; // boosters generated containing cards from multiple sets
|
||||
protected boolean isRichMan; // new boosters generated for each pick
|
||||
protected boolean isJumpstart;
|
||||
|
||||
protected TournamentType() {
|
||||
|
@ -65,6 +66,10 @@ public class TournamentType implements Serializable {
|
|||
return this.isRandom;
|
||||
}
|
||||
|
||||
public boolean isRemixed() {
|
||||
return this.isRemixed;
|
||||
}
|
||||
|
||||
public boolean isRichMan() {
|
||||
return this.isRichMan;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue