Merge pull request #6669 from hitch17/jumpstart-tournament-formats

Add jumpstart swiss and elimination tournament formats.
This commit is contained in:
Oleg Agafonov 2020-06-30 06:29:33 +02:00 committed by GitHub
commit d5616a86f9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 1518 additions and 5 deletions

View file

@ -1203,6 +1203,7 @@ public class NewTournamentDialog extends MageDialog {
tOptions.getLimitedOptions().setConstructionTime((Integer) this.spnConstructTime.getValue() * 60);
tOptions.getLimitedOptions().setIsRandom(tournamentType.isRandom());
tOptions.getLimitedOptions().setIsRichMan(tournamentType.isRichMan());
tOptions.getLimitedOptions().setIsJumpstart(tournamentType.isJumpstart());
if (tournamentType.isCubeBooster()) {
tOptions.getLimitedOptions().setDraftCubeName(this.cbDraftCube.getSelectedItem().toString());
if (!(cubeFromDeckFilename.isEmpty())) {

View file

@ -22,6 +22,7 @@ public class TournamentTypeView implements Serializable {
private final boolean elimination;
private final boolean random;
private final boolean richMan;
private final boolean jumpstart;
public TournamentTypeView(TournamentType tournamentType) {
this.name = tournamentType.getName();
@ -34,6 +35,7 @@ public class TournamentTypeView implements Serializable {
this.elimination = tournamentType.isElimination();
this.random = tournamentType.isRandom();
this.richMan = tournamentType.isRichMan();
this.jumpstart = tournamentType.isJumpstart();
}
@Override
@ -80,4 +82,9 @@ public class TournamentTypeView implements Serializable {
public boolean isRichMan() {
return richMan;
}
public boolean isJumpstart() {
return jumpstart;
}
}

View file

@ -0,0 +1,44 @@
package mage.tournament;
import mage.game.tournament.TournamentOptions;
import mage.game.tournament.TournamentSingleElimination;
public class JumpstartEliminationTournament extends TournamentSingleElimination {
protected enum TournamentStep {
START, OPEN_BOOSTERS, CONSTRUCT, COMPETE, WINNERS
}
protected TournamentStep currentStep;
public JumpstartEliminationTournament(TournamentOptions options) {
super(options);
currentStep = TournamentStep.START;
}
@Override
public void nextStep() {
switch (currentStep) {
case START:
currentStep = TournamentStep.OPEN_BOOSTERS;
openBoosters();
break;
case OPEN_BOOSTERS:
currentStep = TournamentStep.CONSTRUCT;
construct();
break;
case CONSTRUCT:
currentStep = TournamentStep.COMPETE;
runTournament();
break;
case COMPETE:
currentStep = TournamentStep.WINNERS;
winners();
end();
break;
}
}
}

View file

@ -0,0 +1,19 @@
package mage.tournament;
import mage.game.tournament.TournamentType;
public class JumpstartEliminationTournamentType extends TournamentType {
public JumpstartEliminationTournamentType() {
this.name = "Jumpstart Elimination";
this.maxPlayers = 16;
this.minPlayers = 2;
this.numBoosters = 0;
this.isJumpstart = true;
this.elimination = true;
this.limited = true;
}
}

View file

@ -0,0 +1,44 @@
package mage.tournament;
import mage.game.tournament.TournamentOptions;
import mage.game.tournament.TournamentSwiss;
public class JumpstartSwissTournament extends TournamentSwiss {
protected enum TournamentStep {
START, OPEN_BOOSTERS, CONSTRUCT, COMPETE, WINNERS
}
protected TournamentStep currentStep;
public JumpstartSwissTournament(TournamentOptions options) {
super(options);
currentStep = TournamentStep.START;
}
@Override
public void nextStep() {
switch (currentStep) {
case START:
currentStep = TournamentStep.OPEN_BOOSTERS;
openBoosters();
break;
case OPEN_BOOSTERS:
currentStep = TournamentStep.CONSTRUCT;
construct();
break;
case CONSTRUCT:
currentStep = TournamentStep.COMPETE;
runTournament();
break;
case COMPETE:
currentStep = TournamentStep.WINNERS;
winners();
end();
break;
}
}
}

View file

@ -0,0 +1,18 @@
package mage.tournament;
import mage.game.tournament.TournamentType;
public class JumpstartSwissTournamentType extends TournamentType {
public JumpstartSwissTournamentType() {
this.name = "Jumpstart Swiss";
this.maxPlayers = 16;
this.minPlayers = 2;
this.numBoosters = 0;
this.isJumpstart = true;
this.limited = true;
}
}

View file

@ -104,6 +104,8 @@
<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"/>
<tournamentType name="Sealed Swiss (Cube)" jar="mage-tournament-sealed.jar" className="mage.tournament.SealedSwissTournament" typeName="mage.tournament.SealedSwissCubeTournamentType"/>
<tournamentType name="Jumpstart Elimination" jar="mage-tournament-sealed.jar" className="mage.tournament.JumpstartEliminationTournament" typeName="mage.tournament.JumpstartEliminationTournamentType"/>
<tournamentType name="Jumpstart Swiss" jar="mage-tournament-sealed.jar" className="mage.tournament.JumpstartSwissTournament" typeName="mage.tournament.JumpstartSwissTournamentType"/>
</tournamentTypes>
<draftCubes>
<draftCube name="Adam Styborski's Pauper Cube" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.AdamStyborskisPauperCube"/>

View file

@ -98,6 +98,8 @@
<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"/>
<tournamentType name="Sealed Swiss (Cube)" jar="mage-tournament-sealed-${project.version}.jar" className="mage.tournament.SealedSwissTournament" typeName="mage.tournament.SealedSwissCubeTournamentType"/>
<tournamentType name="Jumpstart Elimination" jar="mage-tournament-sealed.jar" className="mage.tournament.JumpstartEliminationTournament" typeName="mage.tournament.JumpstartEliminationTournamentType"/>
<tournamentType name="Jumpstart Swiss" jar="mage-tournament-sealed.jar" className="mage.tournament.JumpstartSwissTournament" typeName="mage.tournament.JumpstartSwissTournamentType"/>
</tournamentTypes>
<draftCubes>
<draftCube name="Adam Styborski's Pauper Cube" jar="mage-tournament-booster-draft-${project.version}.jar" className="mage.tournament.cubes.AdamStyborskisPauperCube"/>

View file

@ -0,0 +1,129 @@
package mage.game.jumpstart;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import com.google.common.base.Charsets;
import com.google.common.io.CharSource;
import com.google.common.io.Resources;
import mage.cards.Card;
import mage.cards.decks.Deck;
import mage.cards.decks.DeckCardInfo;
import mage.cards.decks.DeckCardLists;
import mage.game.GameException;
public class JumpstartPoolGenerator {
/**
* "Jumpstart is a new way to play Magic that mashes together themes from throughout the history of the game and
* lets you skip the deckbuilding part. Grab two boosters, shuffle them into a 40-card deck, and start playing.
* The set contains almost 500 reprints but also introduces 37 new cards that were designed to help fill out some
* of the themes. These are not going to be Standard-, Pioneer-, or Modern-legal cards, but are legal in Eternal
* formats (Legacy, Vintage, Pauper and Commander). There are no foil cards in the set. 120 cards are from Core
* Set 2021[7], and more than 400 reprints are from sets before Core Set 2021. Cards from M21 have the M21 expansion
* symbol, while new cards and other reprints will have the Jumpstart symbol. Reprints have a Post-M15 card frame.
* The new cards and cards with new art are numbered #1/78 to #78/78 (40 basic lands, TBA common, TBA uncommon,
* TBA rare, and TBA mythic rare). The other reprints with the Jumpstart expansion symbol are numbered #79 to 495."
*
* "Jumpstart is sold in 20-card boosters, and booster boxes contain 24 boosters.[9] All 20 cards in a booster fit a
* theme, and most themes are mono-color and have multiple variations of cards included in the pack, totaling 121
* different possible pack contents. Special "Mythic Rare" packs don't have variations at all, but just one possible
* card list and can feature multiple colors. What theme each booster contains is randomized. Most packs are
* singletons, but there are some instances of having two copies of a card in the pack. All boosters contain at
* least one rare, and one in three boosters includes an extra rare. Of the 20 cards in a pack, seven or eight
* are lands. One basic land (or in the case of Rainbow, Terramorphic Expanse) features art that matches the pack's
* theme. The planeswalker themed packs use the respective Showcase lands from M21, but the other packs use brand-new
* themed land art created for the set. The booster pack is wrapped like a regular pack, but the set of cards is
* packed in an additional plastic wrap, with a face card (the "Pack Summary card") that indicates the theme and
* the color of the half-deck."
*
* Source: https://mtg.gamepedia.com/Jumpstart
*
* Announcement: https://magic.wizards.com/en/articles/archive/news/introducing-jumpstart-new-way-play-magic-2020-02-20
* Card Pool: https://magic.wizards.com/en/articles/archive/card-image-gallery/jumpstart
* Deck Lists: https://magic.wizards.com/en/articles/archive/feature/jumpstart-decklists-2020-06-18
*/
private static final String RESOURCE_NAME = "jumpstart/jumpstart.txt";
private static final List<JumpstartPack> JUMPSTART_PACKS;
static {
try {
CharSource source = Resources.asCharSource(Resources.getResource(RESOURCE_NAME), Charsets.UTF_8);
List<JumpstartPack> packs = new ArrayList<>();
JumpstartPack pack = new JumpstartPack();
packs.add(pack);
for (String line : source.readLines()) {
if (line.isEmpty()) {
if (!pack.isEmpty()) {
pack = new JumpstartPack();
packs.add(pack);
}
} else if (line.startsWith("#")) {
// skip comment
} else {
String[] ls = line.split(" ", 4);
int quantity = Integer.parseInt(ls[0]);
for (int i = 0; i < quantity; i++) {
pack.add(new DeckCardInfo(ls[3], ls[2], ls[1]));
}
}
}
JUMPSTART_PACKS = Collections.unmodifiableList(packs);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
/* Notes
*
* 1) the pools generated by this method are for how the prerelease will be played (see https://mtg.gamepedia.com/Jumpstart#Events)
* In order to support the 4 pack version, xmage would need to support editing multiple decks and some Duo format
* similar to https://mtg.gamepedia.com/Duo_Standard
*
* 2) this treats all packs to have similar chance to open (packs will actually be opened at a particular rarity). This
* could be implemented if you know the various ratios between packs by knowing the rarities from this source:
* https://mtg.gamepedia.com/Jumpstart#Themes_and_mechanics
*
* 3) this does not attempt to add an additional rare 1/3 of the time as described here:
* https://mtg.gamepedia.com/Jumpstart#Marketing
*/
public static Set<Card> generatePool() {
try {
DeckCardLists list = new DeckCardLists();
SecureRandom random = new SecureRandom();
for (int i = 0; i < 2; i++) {
int index = random.nextInt(JUMPSTART_PACKS.size());
list.getCards().addAll(JUMPSTART_PACKS.get(index).getCards());
}
return Deck.load(list).getCards();
} catch (GameException e) {
throw new RuntimeException(e);
}
}
public static class JumpstartPack {
private final List<DeckCardInfo> cards = new ArrayList<>();
public void add(DeckCardInfo card) {
cards.add(card);
}
public boolean isEmpty() {
return cards.isEmpty();
}
public List<DeckCardInfo> getCards() {
return Collections.unmodifiableList(cards);
}
}
}

View file

@ -19,7 +19,8 @@ public class LimitedOptions implements Serializable {
protected int numberBoosters;
protected boolean isRandom;
protected boolean isRichMan;
protected Deck cubeFromDeck = null;
protected Deck cubeFromDeck;
protected boolean isJumpstart;
public List<String> getSetCodes() {
return sets;
@ -80,4 +81,13 @@ public class LimitedOptions implements Serializable {
public void setIsRichMan(boolean isRichMan) {
this.isRichMan = isRichMan;
}
public void setIsJumpstart(boolean isJumpstart) {
this.isJumpstart = isJumpstart;
}
public boolean getIsJumpstart() {
return this.isJumpstart;
}
}

View file

@ -1,15 +1,32 @@
package mage.game.tournament;
import java.util.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArrayList;
import org.apache.log4j.Logger;
import mage.cards.ExpansionSet;
import mage.cards.decks.Deck;
import mage.constants.TournamentPlayerState;
import mage.game.draft.Draft;
import mage.game.draft.DraftCube;
import mage.game.events.*;
import mage.game.events.Listener;
import mage.game.events.PlayerQueryEvent;
import mage.game.events.PlayerQueryEventSource;
import mage.game.events.TableEvent;
import mage.game.events.TableEvent.EventType;
import mage.game.events.TableEventSource;
import mage.game.jumpstart.JumpstartPoolGenerator;
import mage.game.match.Match;
import mage.game.match.MatchPlayer;
import mage.game.result.ResultProtos.MatchPlayerProto;
@ -20,7 +37,6 @@ import mage.game.result.ResultProtos.TourneyRoundProto;
import mage.players.Player;
import mage.players.PlayerType;
import mage.util.RandomUtil;
import org.apache.log4j.Logger;
/**
*
@ -394,6 +410,8 @@ public abstract class TournamentImpl implements Tournament {
for (int i = 0; i < options.getLimitedOptions().getNumberBoosters(); i++) {
player.getDeck().getSideboard().addAll(cube.createBooster());
}
} else if (options.getLimitedOptions().getIsJumpstart()) {
player.getDeck().getCards().addAll(JumpstartPoolGenerator.generatePool());
} else {
for (ExpansionSet set : sets) {
player.getDeck().getSideboard().addAll(set.createBooster());

View file

@ -18,7 +18,8 @@ public class TournamentType implements Serializable {
protected boolean limited; // or construced
protected boolean elimination; // or Swiss
protected boolean isRandom;
protected boolean isRichMan = false; // or Rich Man Draft
protected boolean isRichMan; // or Rich Man Draft
protected boolean isJumpstart;
protected TournamentType() {
}
@ -68,4 +69,8 @@ public class TournamentType implements Serializable {
return this.isRichMan;
}
public boolean isJumpstart() {
return this.isJumpstart;
}
}

File diff suppressed because it is too large Load diff