Merge pull request #5503 from hitch17/add-cockatrice-deck-format-5493

Adding o8d deck format.
This commit is contained in:
Oleg Agafonov 2019-01-11 08:14:38 +04:00 committed by GitHub
commit c3848b7530
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 391 additions and 118 deletions

View file

@ -1123,7 +1123,8 @@ class ImportFilter extends FileFilter {
|| ext.toLowerCase(Locale.ENGLISH).equals("mwdeck")
|| ext.toLowerCase(Locale.ENGLISH).equals("txt")
|| ext.toLowerCase(Locale.ENGLISH).equals("dek")
|| ext.toLowerCase(Locale.ENGLISH).equals("cod")) {
|| ext.toLowerCase(Locale.ENGLISH).equals("cod")
|| ext.toLowerCase(Locale.ENGLISH).equals("o8d")) {
return true;
}
}
@ -1132,7 +1133,7 @@ class ImportFilter extends FileFilter {
@Override
public String getDescription() {
return "*.dec | *.mwDeck | *.txt | *.dek | *.cod";
return "*.dec | *.mwDeck | *.txt | *.dek | *.cod | *.o8d";
}
}

View file

@ -1,7 +1,9 @@
package mage.cards.decks.importer;
import java.util.List;
import java.util.Optional;
import mage.cards.repository.CardCriteria;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
@ -13,4 +15,8 @@ public class CardLookup {
return Optional.ofNullable(CardRepository.instance.findPreferedCoreExpansionCard(name, true));
}
public List<CardInfo> lookupCardInfo(CardCriteria criteria) {
return CardRepository.instance.findCards(criteria);
}
}

View file

@ -1,10 +1,5 @@
package mage.cards.decks.importer;
import static javax.xml.xpath.XPathConstants.NODESET;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
@ -12,26 +7,14 @@ import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import mage.cards.decks.DeckCardInfo;
import mage.cards.decks.DeckCardLists;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
public class CodDeckImporter extends DeckImporter {
private XPathFactory xpathFactory = XPathFactory.newInstance();
private DocumentBuilder builder = getDocumentBuilder();
public class CodDeckImporter extends XmlDeckImporter {
@Override
public DeckCardLists importDeck(String filename, StringBuilder errorMessages) {
@ -88,30 +71,4 @@ public class CodDeckImporter extends DeckImporter {
};
}
private List<Node> getNodes(Document doc, String xpathExpression) throws XPathExpressionException {
NodeList nodes = (NodeList) xpathFactory.newXPath().evaluate(xpathExpression, doc, NODESET);
ArrayList<Node> list = new ArrayList<>();
for (int i = 0; i < nodes.getLength(); i++) {
list.add(nodes.item(i));
}
return list;
}
private DocumentBuilder getDocumentBuilder() {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
return factory.newDocumentBuilder();
} catch (ParserConfigurationException e) {
throw new RuntimeException();
}
}
protected Document getXmlDocument(String filename) throws IOException {
try {
return builder.parse(new File(filename));
} catch (SAXException e) {
throw new IOException(e);
}
}
}

View file

@ -32,6 +32,8 @@ public abstract class DeckImporter {
return new DekDeckImporter();
} else if (file.toLowerCase(Locale.ENGLISH).endsWith("cod")) {
return new CodDeckImporter();
} else if (file.toLowerCase(Locale.ENGLISH).endsWith("o8d")) {
return new O8dDeckImporter();
} else {
return null;
}

View file

@ -43,13 +43,13 @@ public class MWSDeckImporter extends PlainTextDeckImporter {
CardCriteria criteria = new CardCriteria();
criteria.name(lineName);
criteria.setCodes(setCode);
List<CardInfo> cards = CardRepository.instance.findCards(criteria);
List<CardInfo> cards = getCardLookup().lookupCardInfo(criteria);
if (!cards.isEmpty()) {
cardInfo = cards.get(RandomUtil.nextInt(cards.size()));
}
}
if (cardInfo == null) {
cardInfo = CardRepository.instance.findPreferedCoreExpansionCard(lineName, true);
cardInfo = getCardLookup().lookupCardInfo(lineName).orElse(null);
}
if (cardInfo == null) {

View file

@ -0,0 +1,71 @@
package mage.cards.decks.importer;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import mage.cards.decks.DeckCardInfo;
import mage.cards.decks.DeckCardLists;
import mage.cards.repository.CardInfo;
public class O8dDeckImporter extends XmlDeckImporter {
@Override
public DeckCardLists importDeck(String filename, StringBuilder errorMessages) {
try {
Document doc = getXmlDocument(filename);
DeckCardLists decklist = new DeckCardLists();
List<Node> mainCards = getNodes(doc, "/deck/section[@name='Main']/card");
decklist.setCards(mainCards.stream()
.flatMap(toDeckCardInfo(getCardLookup(), errorMessages))
.collect(Collectors.toList()));
List<Node> sideboardCards = getNodes(doc, "/deck/section[@name='Sideboard']/card");
decklist.setSideboard(sideboardCards.stream()
.flatMap(toDeckCardInfo(getCardLookup(), errorMessages))
.collect(Collectors.toList()));
return decklist;
} catch (Exception e) {
logger.error("Error loading deck", e);
errorMessages.append("There was an error loading the deck.");
return new DeckCardLists();
}
}
private static int getQuantityFromNode(Node node) {
Node numberNode = node.getAttributes().getNamedItem("qty");
if (numberNode == null) {
return 1;
}
try {
return Math.min(100, Math.max(1, Integer.parseInt(numberNode.getNodeValue())));
} catch (NumberFormatException e) {
return 1;
}
}
private static Function<Node, Stream<DeckCardInfo>> toDeckCardInfo(CardLookup lookup, StringBuilder errors) {
return node -> {
String name = node.getTextContent();
Optional<CardInfo> cardInfo = lookup.lookupCardInfo(name);
if (cardInfo.isPresent()) {
CardInfo info = cardInfo.get();
return Collections.nCopies(
getQuantityFromNode(node),
new DeckCardInfo(info.getName(), info.getCardNumber(), info.getSetCode())).stream();
} else {
errors.append("Could not find card: '").append(name).append("'\n");
return Stream.empty();
}
};
}
}

View file

@ -0,0 +1,52 @@
package mage.cards.decks.importer;
import static javax.xml.xpath.XPathConstants.NODESET;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
public abstract class XmlDeckImporter extends DeckImporter {
private XPathFactory xpathFactory = XPathFactory.newInstance();
private DocumentBuilder builder = getDocumentBuilder();
protected List<Node> getNodes(Document doc, String xpathExpression) throws XPathExpressionException {
NodeList nodes = (NodeList) xpathFactory.newXPath().evaluate(xpathExpression, doc, NODESET);
ArrayList<Node> list = new ArrayList<>();
for (int i = 0; i < nodes.getLength(); i++) {
list.add(nodes.item(i));
}
return list;
}
private DocumentBuilder getDocumentBuilder() {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
return factory.newDocumentBuilder();
} catch (ParserConfigurationException e) {
throw new RuntimeException();
}
}
protected Document getXmlDocument(String filename) throws IOException {
try {
return builder.parse(new File(filename));
} catch (SAXException e) {
throw new IOException(e);
}
}
}

View file

@ -309,4 +309,97 @@ public class CardCriteria {
qb.orderBy(sortBy, true);
}
}
public String getName() {
return name;
}
public String getNameExact() {
return nameExact;
}
public String getRules() {
return rules;
}
public List<String> getSetCodes() {
return setCodes;
}
public List<CardType> getTypes() {
return types;
}
public List<CardType> getNotTypes() {
return notTypes;
}
public List<String> getSupertypes() {
return supertypes;
}
public List<String> getNotSupertypes() {
return notSupertypes;
}
public List<String> getSubtypes() {
return subtypes;
}
public List<Rarity> getRarities() {
return rarities;
}
public Boolean getDoubleFaced() {
return doubleFaced;
}
public boolean isBlack() {
return black;
}
public boolean isBlue() {
return blue;
}
public boolean isGreen() {
return green;
}
public boolean isRed() {
return red;
}
public boolean isWhite() {
return white;
}
public boolean isColorless() {
return colorless;
}
public Integer getConvertedManaCost() {
return convertedManaCost;
}
public String getSortBy() {
return sortBy;
}
public Long getStart() {
return start;
}
public Long getCount() {
return count;
}
public int getMinCardNumber() {
return minCardNumber;
}
public int getMaxCardNumber() {
return maxCardNumber;
}
}

View file

@ -2,31 +2,20 @@ package mage.cards.decks.importer;
import static org.junit.Assert.assertEquals;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.junit.Test;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.decks.DeckCardInfo;
import mage.cards.decks.DeckCardLists;
import mage.cards.repository.CardInfo;
public class CodDeckImportTest {
private static final FakeCardLookup LOOKUP = new FakeCardLookup()
.addCard("Forest")
.addCard("Razorverge Thicket")
.addCard("Avacyn's Pilgrim")
.addCard("War Priest of Thune");
private static final FakeCardLookup LOOKUP = new FakeCardLookup(false)
.addCard("Forest")
.addCard("Razorverge Thicket")
.addCard("Avacyn's Pilgrim")
.addCard("War Priest of Thune");
@Test
public void testImportCod() {
public void testImport() {
CodDeckImporter importer = new CodDeckImporter() {
@Override
public CardLookup getCardLookup() {
@ -35,7 +24,7 @@ public class CodDeckImportTest {
};
StringBuilder errors = new StringBuilder();
DeckCardLists deck = importer.importDeck(
"src/test/java/mage/cards/decks/importer/testdeck.cod", errors);
"src/test/java/mage/cards/decks/importer/samples/testdeck.cod", errors);
assertEquals("Deck Name", deck.getName());
TestDeckChecker.checker()
@ -48,9 +37,4 @@ public class CodDeckImportTest {
assertEquals("Could not find card: '@#$NOT A REAL CARD NAME@#$'\n", errors.toString());
}
private static void assertCardSame(String name, DeckCardInfo card) {
assertEquals(name, card.getCardName());
assertEquals(1, card.getQuantity());
}
}

View file

@ -2,41 +2,13 @@ package mage.cards.decks.importer;
import static org.junit.Assert.assertEquals;
import java.util.Collections;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.junit.Test;
import mage.cards.decks.DeckCardInfo;
import mage.cards.decks.DeckCardLists;
import mage.cards.repository.CardInfo;
public class DecDeckImportTest {
private static final FakeCardLookup LOOKUP = new FakeCardLookup()
.addCard("Masticore")
.addCard("Metalworker")
.addCard("Phyrexian Colossus")
.addCard("Crumbling Sanctuary")
.addCard("Grim Monolith")
.addCard("Mishra's Helix")
.addCard("Phyrexian Processor")
.addCard("Tangle Wire")
.addCard("Thran Dynamo")
.addCard("Voltaic Key")
.addCard("Tinker")
.addCard("Brainstorm")
.addCard("Crystal Vein")
.addCard("Island")
.addCard("Rishadan Port")
.addCard("Saprazzan Skerry")
.addCard("Annul")
.addCard("Chill")
.addCard("Miscalculation")
.addCard("Mishra's Helix")
.addCard("Rising Waters");
private static final FakeCardLookup LOOKUP = new FakeCardLookup();
@Test
public void testImport() {
@ -48,7 +20,7 @@ public class DecDeckImportTest {
}
};
DeckCardLists deck = importer.importDeck(
"src/test/java/mage/cards/decks/importer/testdeck.dec", errors);
"src/test/java/mage/cards/decks/importer/samples/testdeck.dec", errors);
TestDeckChecker.checker()
.addMain("Masticore", 4)
@ -77,15 +49,4 @@ public class DecDeckImportTest {
assertEquals("", errors.toString());
}
private static FakeCardLookup getFakeCardLookup() {
FakeCardLookup lookup = new FakeCardLookup() {
@Override
public Optional<CardInfo> lookupCardInfo(String name) {
System.out.println(name);
return super.lookupCardInfo(name);
}
};
return lookup;
}
}

View file

@ -1,14 +1,26 @@
package mage.cards.decks.importer;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import mage.cards.repository.CardCriteria;
import mage.cards.repository.CardInfo;
public class FakeCardLookup extends CardLookup {
private final Map<String, CardInfo> lookup = new HashMap<>();
private final boolean alwaysMatches;
public FakeCardLookup() {
this(true);
}
public FakeCardLookup(boolean alwaysMatches) {
this.alwaysMatches = alwaysMatches;
}
public FakeCardLookup addCard(String cardName) {
lookup.put(cardName, new CardInfo() {{
@ -17,13 +29,26 @@ public class FakeCardLookup extends CardLookup {
return this;
}
@Override
public Optional<CardInfo> lookupCardInfo(String name) {
CardInfo card = lookup.get(name);
if (card == null) {
System.out.println("Couldn't find: " + name);
public Optional<CardInfo> lookupCardInfo(String cardName) {
CardInfo card = lookup.get(cardName);
if (card != null) {
return Optional.of(card);
}
return Optional.ofNullable(card);
if (alwaysMatches) {
return Optional.of(new CardInfo() {{
name = cardName;
}});
}
return Optional.empty();
}
@Override
public List<CardInfo> lookupCardInfo(CardCriteria criteria) {
return lookupCardInfo(criteria.getName())
.map(Collections::singletonList)
.orElse(Collections.emptyList());
}
}

View file

@ -0,0 +1,57 @@
package mage.cards.decks.importer;
import static org.junit.Assert.assertEquals;
import java.util.Collections;
import java.util.List;
import org.junit.Test;
import mage.cards.decks.DeckCardLists;
import mage.cards.repository.CardCriteria;
import mage.cards.repository.CardInfo;
public class MwsDeckImportTest {
private static final FakeCardLookup LOOKUP = new FakeCardLookup();
@Test
public void testImport() {
MWSDeckImporter importer = new MWSDeckImporter() {
@Override
public CardLookup getCardLookup() {
return LOOKUP;
}
};
StringBuilder errors = new StringBuilder();
DeckCardLists deck = importer.importDeck(
"src/test/java/mage/cards/decks/importer/samples/testdeck.mwDeck", errors);
TestDeckChecker.checker()
.addMain("Mutavault", 4)
.addMain("Plains", 18)
.addMain("Daring Skyjek", 2)
.addMain("Azorius Arrester", 4)
.addMain("Banisher Priest", 4)
.addMain("Boros Elite", 4)
.addMain("Dryad Militant", 4)
.addMain("Imposing Sovereign", 4)
.addMain("Precinct Captain", 4)
.addMain("Soldier of the Pantheon", 4)
.addMain("Spear of Heliod", 3)
.addMain("Rootborn Defenses", 1)
.addMain("Brave the Elements", 4)
.addSide("Wear/Tear", 1)
.addSide("Glare of Heresy", 2)
.addSide("Fiendslayer Paladin", 3)
.addSide("Riot Control", 3)
.addSide("Ajani, Caller of the Pride", 3)
.addSide("Rootborn Defenses", 3)
.verify(deck, 60, 15);
assertEquals("", errors.toString());
}
}

View file

@ -0,0 +1,33 @@
package mage.cards.decks.importer;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import mage.cards.decks.DeckCardLists;
public class O8dDeckImportTest {
private static final FakeCardLookup LOOKUP = new FakeCardLookup();
@Test
public void testImport() {
O8dDeckImporter importer = new O8dDeckImporter() {
@Override
public CardLookup getCardLookup() {
return LOOKUP;
}
};
StringBuilder errors = new StringBuilder();
DeckCardLists deck = importer.importDeck(
"src/test/java/mage/cards/decks/importer/samples/testdeck.o8d", errors);
TestDeckChecker.checker()
.addMain("Forest", 1)
.addSide("Island", 2)
.verify(deck, 1, 2);
assertEquals("", errors.toString());
}
}

View file

@ -0,0 +1,23 @@
// Deck file for Magic Workstation (http://www.magicworkstation.com)
// NAME : WW Human
// CREATOR : meltiin (magic-ville.com)
// FORMAT : Standard
4 [M14] Mutavault
18 [UNH] Plains
2 [GTC] Daring Skyjek
4 [RTR] Azorius Arrester
4 [M14] Banisher Priest
4 [GTC] Boros Elite
4 [RTR] Dryad Militant
4 [M14] Imposing Sovereign
4 [RTR] Precinct Captain
4 [THS] Soldier of the Pantheon
3 [THS] Spear of Heliod
1 [RTR] Rootborn Defenses
4 [M14] Brave the Elements
SB: 1 [DGM] Wear/Tear
SB: 2 [THS] Glare of Heresy
SB: 3 [M14] Fiendslayer Paladin
SB: 3 [DGM] Riot Control
SB: 3 [M14] Ajani, Caller of the Pride
SB: 3 [RTR] Rootborn Defenses

View file

@ -0,0 +1,8 @@
<deck>
<section name="Main">
<card qty="1">Forest</card>
</section>
<section name="Sideboard">
<card qty="2">Island</card>
</section>
</deck>