diff options
18 files changed, 301 insertions, 69 deletions
diff --git a/deck.cbd b/deck.cbd new file mode 100644 index 0000000..f3dac02 --- /dev/null +++ b/deck.cbd @@ -0,0 +1 @@ +{"cards":[{"name":"Awe for the Guilds","layout":"normal","manaCost":"{2}{R}","cmc":3,"type":"Sorcery","rarity":"Common","text":"Monocolored creatures can't block this turn.","flavor":"When the guilds cooperate, the guildless celebrate their peaceful society. When the guilds clash, the guildless just try to keep out of the way.","artist":"Mathias Kollros","number":"31","power":null,"toughness":null,"loyalty":null,"multiverseid":369053,"imageName":"awe for the guilds","border":null,"watermark":null,"setCode":"DGM","imageCode":"dgm","count":1},{"name":"Brindle Boar","layout":"normal","manaCost":"{2}{G}","cmc":3,"type":"Creature — Boar","rarity":"Common","text":"Sacrifice Brindle Boar: You gain 4 life.","flavor":"\"Tell the cooks to prepare the fires. Tonight we feast!\"\n—Tolar Wolfbrother, Krosan tracker","artist":"Dave Allsop","number":"167","power":"2","toughness":"2","loyalty":null,"multiverseid":370778,"imageName":"brindle boar","border":null,"watermark":null,"setCode":"M14","imageCode":"m14","count":1},{"name":"Kalonian Tusker","layout":"normal","manaCost":"{G}{G}","cmc":2,"type":"Creature — Beast","rarity":"Uncommon","text":null,"flavor":"\"And all this time I thought we were tracking it.\"\n—Juruk, Kalonian tracker","artist":"Svetlin Velinov","number":"182","power":"3","toughness":"3","loyalty":null,"multiverseid":370700,"imageName":"kalonian tusker","border":null,"watermark":null,"setCode":"M14","imageCode":"m14","count":1},{"name":"Nessian Asp","layout":"normal","manaCost":"{4}{G}","cmc":5,"type":"Creature — Snake","rarity":"Common","text":"Reach\n{6}{G}: Monstrosity 4. (If this creature isn't monstrous, put four +1/+1 counters on it and it becomes monstrous.)","flavor":"It's not the two heads you should fear. It's the four fangs.","artist":"Alex Horley-Orlandelli","number":"164","power":"4","toughness":"5","loyalty":null,"multiverseid":373650,"imageName":"nessian asp","border":null,"watermark":null,"setCode":"THS","imageCode":"ths","count":2},{"name":"Lightning Strike","layout":"normal","manaCost":"{1}{R}","cmc":2,"type":"Instant","rarity":"Common","text":"Lightning Strike deals 3 damage to target creature or player.","flavor":"\"The hand of Keranos can be seen in every rumbling storm cloud. Best not to stand where he points.\"\n—Rakleia of Shrine Peak","artist":"Adam Paquette","number":"127","power":null,"toughness":null,"loyalty":null,"multiverseid":373651,"imageName":"lightning strike","border":null,"watermark":null,"setCode":"THS","imageCode":"ths","count":2},{"name":"Dragon Mantle","layout":"normal","manaCost":"{R}","cmc":1,"type":"Enchantment — Aura","rarity":"Common","text":"Enchant creature\nWhen Dragon Mantle enters the battlefield, draw a card.\nEnchanted creature has \"{R}: This creature gets +1/+0 until end of turn.\"","flavor":null,"artist":"Anthony Palumbo","number":"119","power":null,"toughness":null,"loyalty":null,"multiverseid":373634,"imageName":"dragon mantle","border":null,"watermark":null,"setCode":"THS","imageCode":"ths","count":2},{"name":"Voyaging Satyr","layout":"normal","manaCost":"{1}{G}","cmc":2,"type":"Creature — Satyr Druid","rarity":"Common","text":"{T}: Untap target land.","flavor":"\"None can own the land's bounty. The gods made this world for all to share its riches. And I'm not just saying that because you caught me stealing your fruit.\"","artist":"Tyler Jacobson","number":"182","power":"1","toughness":"2","loyalty":null,"multiverseid":373518,"imageName":"voyaging satyr","border":null,"watermark":null,"setCode":"THS","imageCode":"ths","count":1},{"name":"Satyr Hedonist","layout":"normal","manaCost":"{1}{G}","cmc":2,"type":"Creature — Satyr","rarity":"Common","text":"{R}, Sacrifice Satyr Hedonist: Add {R}{R}{R} to your mana pool.","flavor":"\"Any festival you can walk away from wasn't worth attending in the first place.\"","artist":"Chase Stone","number":"174","power":"2","toughness":"1","loyalty":null,"multiverseid":373744,"imageName":"satyr hedonist","border":null,"watermark":null,"setCode":"THS","imageCode":"ths","count":1},{"name":"Searing Blood","layout":"normal","manaCost":"{R}{R}","cmc":2,"type":"Instant","rarity":"Uncommon","text":"Searing Blood deals 2 damage to target creature. When that creature dies this turn, Searing Blood deals 3 damage to the creature's controller.","flavor":"Purphoros's blessing: sometimes a boon, sometimes a bane. Always ablaze.","artist":"Daniel Ljunggren","number":"111","power":null,"toughness":null,"loyalty":null,"multiverseid":378483,"imageName":"searing blood","border":null,"watermark":null,"setCode":"BNG","imageCode":"bng","count":1},{"name":"Karametra's Favor","layout":"normal","manaCost":"{1}{G}","cmc":2,"type":"Enchantment — Aura","rarity":"Common","text":"Enchant creature\nWhen Karametra's Favor enters the battlefield, draw a card.\nEnchanted creature has \"{T}: Add one mana of any color to your mana pool.\"","flavor":"The harvest god's cornucopia contains the fruits of the fields, the forest, and beyond.","artist":"Chase Stone","number":"125","power":null,"toughness":null,"loyalty":null,"multiverseid":378497,"imageName":"karametra's favor","border":null,"watermark":null,"setCode":"BNG","imageCode":"bng","count":1},{"name":"Magma Spray","layout":"normal","manaCost":"{R}","cmc":1,"type":"Instant","rarity":"Common","text":"Magma Spray deals 2 damage to target creature. If that creature would die this turn, exile it instead.","flavor":"The ancient dragon Thraxes sleeps in Purphoros's sacred peak. When he stirs in dreams, so does the mountain.","artist":"Richard Wright","number":"103","power":null,"toughness":null,"loyalty":null,"multiverseid":380452,"imageName":"magma spray","border":null,"watermark":null,"setCode":"JOU","imageCode":"jou","count":1},{"name":"Market Festival","layout":"normal","manaCost":"{3}{G}","cmc":4,"type":"Enchantment — Aura","rarity":"Common","text":"Enchant land\nWhenever enchanted land is tapped for mana, its controller adds two mana in any combination of colors to his or her mana pool (in addition to the mana the land produces).","flavor":"Commerce is always the basis for peace.","artist":"Ryan Barger","number":"130","power":null,"toughness":null,"loyalty":null,"multiverseid":380454,"imageName":"market festival","border":null,"watermark":null,"setCode":"JOU","imageCode":"jou","count":2},{"name":"Evolving Wilds","layout":"normal","manaCost":null,"cmc":null,"type":"Land","rarity":"Common","text":"{T}, Sacrifice Evolving Wilds: Search your library for a basic land card and put it onto the battlefield tapped. Then shuffle your library.","flavor":"Without the interfering hands of civilization, nature will always shape itself to its own needs.","artist":"Steven Belledin","number":"243","power":null,"toughness":null,"loyalty":null,"multiverseid":383235,"imageName":"evolving wilds","border":null,"watermark":null,"setCode":"M15","imageCode":"m15","count":2},{"name":"Elvish Mystic","layout":"normal","manaCost":"{G}","cmc":1,"type":"Creature — Elf Druid","rarity":"Common","text":"{T}: Add {G} to your mana pool.","flavor":"\"Life grows everywhere. My kin merely find those places where it grows strongest.\"\n—Nissa Revane","artist":"Wesley Burt","number":"173","power":"1","toughness":"1","loyalty":null,"multiverseid":383229,"imageName":"elvish mystic","border":null,"watermark":null,"setCode":"M15","imageCode":"m15","count":2},{"name":"Terra Stomper","layout":"normal","manaCost":"{3}{G}{G}{G}","cmc":6,"type":"Creature — Beast","rarity":"Rare","text":"Terra Stomper can't be countered.\nTrample (If this creature would assign enough damage to its blockers to destroy them, you may have it assign the rest of its damage to defending player or planeswalker.)","flavor":"Its footfalls cause violent earthquakes, hurtling boulders, and unseasonable dust storms.","artist":"Goran Josic","number":"284","power":"8","toughness":"8","loyalty":null,"multiverseid":383173,"imageName":"terra stomper","border":null,"watermark":null,"setCode":"M15","imageCode":"m15","count":1},{"name":"Titanic Growth","layout":"normal","manaCost":"{1}{G}","cmc":2,"type":"Instant","rarity":"Common","text":"Target creature gets +4/+4 until end of turn.","flavor":"The massive dominate through might. The tiny survive with guile. Beware the tiny who become massive.","artist":"Ryan Pancoast","number":"203","power":null,"toughness":null,"loyalty":null,"multiverseid":383415,"imageName":"titanic growth","border":null,"watermark":null,"setCode":"M15","imageCode":"m15","count":1},{"name":"Hammerhand","layout":"normal","manaCost":"{R}","cmc":1,"type":"Enchantment — Aura","rarity":"Common","text":"Enchant creature\nWhen Hammerhand enters the battlefield, target creature can't block this turn.\nEnchanted creature gets +1/+1 and has haste. (It can attack and {T} no matter when it came under your control.)","flavor":null,"artist":"Tomasz Jedruszek","number":"147","power":null,"toughness":null,"loyalty":null,"multiverseid":383262,"imageName":"hammerhand","border":null,"watermark":null,"setCode":"M15","imageCode":"m15","count":1},{"name":"Shivan Dragon","layout":"normal","manaCost":"{4}{R}{R}","cmc":6,"type":"Creature — Dragon","rarity":"Rare","text":"Flying (This creature can't be blocked except by creatures with flying or reach.)\n{R}: Shivan Dragon gets +1/+0 until end of turn.","flavor":"The undisputed master of the mountains of Shiv.","artist":"Donato Giancola","number":"281","power":"5","toughness":"5","loyalty":null,"multiverseid":383172,"imageName":"shivan dragon","border":null,"watermark":null,"setCode":"M15","imageCode":"m15","count":1},{"name":"Valley Dasher","layout":"normal","manaCost":"{1}{R}","cmc":2,"type":"Creature — Human Berserker","rarity":"Common","text":"Haste\nValley Dasher attacks each turn if able.","flavor":"Mardu riders' greatest fear is that a battle might end before their weapons draw blood.","artist":"Matt Stewart","number":"125","power":"2","toughness":"2","loyalty":null,"multiverseid":386712,"imageName":"valley dasher","border":null,"watermark":"Mardu","setCode":"KTK","imageCode":"ktk","count":1},{"name":"Monastery Swiftspear","layout":"normal","manaCost":"{R}","cmc":1,"type":"Creature — Human Monk","rarity":"Uncommon","text":"Haste\nProwess (Whenever you cast a noncreature spell, this creature gets +1/+1 until end of turn.)","flavor":"The calligraphy of combat is written with strokes of sudden blood.","artist":"Steve Argyle","number":"118","power":"1","toughness":"2","loyalty":null,"multiverseid":386608,"imageName":"monastery swiftspear","border":null,"watermark":"Jeskai","setCode":"KTK","imageCode":"ktk","count":1},{"name":"Alpine Grizzly","layout":"normal","manaCost":"{2}{G}","cmc":3,"type":"Creature — Bear","rarity":"Common","text":null,"flavor":"The Temur welcome bears into the clan, fighting alongside them in battle. The relationship dates back to when they labored side by side under Sultai rule.","artist":"John Severin Brassell","number":"127","power":"4","toughness":"2","loyalty":null,"multiverseid":386474,"imageName":"alpine grizzly","border":null,"watermark":null,"setCode":"KTK","imageCode":"ktk","count":1},{"name":"Rugged Highlands","layout":"normal","manaCost":null,"cmc":null,"type":"Land","rarity":"Common","text":"Rugged Highlands enters the battlefield tapped.\nWhen Rugged Highlands enters the battlefield, you gain 1 life.\n{T}: Add {R} or {G} to your mana pool.","flavor":null,"artist":"Eytan Zana","number":"240","power":null,"toughness":null,"loyalty":null,"multiverseid":386641,"imageName":"rugged highlands","border":null,"watermark":null,"setCode":"KTK","imageCode":"ktk","count":1},{"name":"Lightning Shrieker","layout":"normal","manaCost":"{4}{R}","cmc":5,"type":"Creature — Dragon","rarity":"Common","text":"Flying, trample, haste\nAt the beginning of the end step, Lightning Shrieker's owner shuffles it into his or her library.","flavor":"Dragonslayers learned to keep silent about their deeds after seeing the terrible vengeance wrought by Kolaghan and her brood.","artist":"Slawomir Maniak","number":"106","power":"5","toughness":"5","loyalty":null,"multiverseid":391868,"imageName":"lightning shrieker","border":null,"watermark":"Kolaghan","setCode":"FRF","imageCode":"frf","count":1},{"name":"Collateral Damage","layout":"normal","manaCost":"{R}","cmc":1,"type":"Instant","rarity":"Common","text":"As an additional cost to cast Collateral Damage, sacrifice a creature.\nCollateral Damage deals 3 damage to target creature or player.","flavor":"It is much easier to create fire than to contain it.","artist":"Ryan Barger","number":"95","power":null,"toughness":null,"loyalty":null,"multiverseid":391811,"imageName":"collateral damage","border":null,"watermark":null,"setCode":"FRF","imageCode":"frf","count":1},{"name":"Vaultbreaker","layout":"normal","manaCost":"{3}{R}","cmc":4,"type":"Creature — Orc Rogue","rarity":"Uncommon","text":"Whenever Vaultbreaker attacks, you may discard a card. If you do, draw a card.\nDash {2}{R} (You may cast this spell for its dash cost. If you do, it gains haste, and it's returned from the battlefield to its owner's hand at the beginning of the next end step.)","flavor":null,"artist":"Wayne Reynolds","number":"117","power":"4","toughness":"2","loyalty":null,"multiverseid":391951,"imageName":"vaultbreaker","border":null,"watermark":"Mardu","setCode":"FRF","imageCode":"frf","count":1},{"name":"Segmented Krotiq","layout":"normal","manaCost":"{5}{G}","cmc":6,"type":"Creature — Insect","rarity":"Common","text":"Megamorph {6}{G} (You may cast this card face down as a 2/2 creature for {3}. Turn it face up any time for its megamorph cost and put a +1/+1 counter on it.)","flavor":"The list of things a krotiq eats is as long as the krotiq itself.","artist":"Christopher Moeller","number":"202","power":"6","toughness":"5","loyalty":null,"multiverseid":394684,"imageName":"segmented krotiq","border":null,"watermark":null,"setCode":"DTK","imageCode":"dtk","count":2},{"name":"Sprinting Warbrute","layout":"normal","manaCost":"{4}{R}","cmc":5,"type":"Creature — Ogre Berserker","rarity":"Common","text":"Sprinting Warbrute attacks each turn if able.\nDash {3}{R} (You may cast this spell for its dash cost. If you do, it gains haste, and it's returned from the battlefield to its owner's hand at the beginning of the next end step.)","flavor":null,"artist":"Lake Hurwitz","number":"157","power":"5","toughness":"4","loyalty":null,"multiverseid":394709,"imageName":"sprinting warbrute","border":null,"watermark":"Kolaghan","setCode":"DTK","imageCode":"dtk","count":3},{"name":"Harbinger of the Hunt","layout":"normal","manaCost":"{3}{R}{G}","cmc":5,"type":"Creature — Dragon","rarity":"Rare","text":"Flying\n{2}{R}: Harbinger of the Hunt deals 1 damage to each creature without flying.\n{2}{G}: Harbinger of the Hunt deals 1 damage to each other creature with flying.","flavor":"An Atarka dragon's exhale cooks what its inhale consumes.","artist":"Aaron Miller","number":"223","power":"5","toughness":"3","loyalty":null,"multiverseid":394591,"imageName":"harbinger of the hunt","border":null,"watermark":"Atarka","setCode":"DTK","imageCode":"dtk","count":1},{"name":"Surrak, the Hunt Caller","layout":"normal","manaCost":"{2}{G}{G}","cmc":4,"type":"Legendary Creature — Human Warrior","rarity":"Rare","text":"Formidable — At the beginning of combat on your turn, if creatures you control have total power 8 or greater, target creature you control gains haste until end of turn.","flavor":"\"The greatest honor is to feed Atarka.\"","artist":"Wesley Burt","number":"210","power":"5","toughness":"4","loyalty":null,"multiverseid":394721,"imageName":"surrak, the hunt caller","border":null,"watermark":"Atarka","setCode":"DTK","imageCode":"dtk","count":1},{"name":"Kolaghan Aspirant","layout":"normal","manaCost":"{1}{R}","cmc":2,"type":"Creature — Human Warrior","rarity":"Common","text":"Whenever Kolaghan Aspirant becomes blocked by a creature, Kolaghan Aspirant deals 1 damage to that creature.","flavor":"She answers the call of the Crave, the desire for battle sated only by bloodshed.","artist":"Aaron Miller","number":"143","power":"2","toughness":"1","loyalty":null,"multiverseid":394608,"imageName":"kolaghan aspirant","border":null,"watermark":"Kolaghan","setCode":"DTK","imageCode":"dtk","count":1},{"name":"Dragon Whisperer","layout":"normal","manaCost":"{R}{R}","cmc":2,"type":"Creature — Human Shaman","rarity":"Mythic Rare","text":"{R}: Dragon Whisperer gains flying until end of turn.\n{1}{R}: Dragon Whisperer gets +1/+0 until end of turn.\nFormidable — {4}{R}{R}: Put a 4/4 red Dragon creature token with flying onto the battlefield. Activate this ability only if creatures you control have total power 8 or greater.","flavor":null,"artist":"Chris Rallis","number":"137","power":"2","toughness":"2","loyalty":null,"multiverseid":394543,"imageName":"dragon whisperer","border":null,"watermark":"Atarka","setCode":"DTK","imageCode":"dtk","count":1}],"plains":0,"islands":0,"swamps":0,"mountains":11,"forests":9,"name":"Red-green Ramp"}
\ No newline at end of file diff --git a/src/eu/equalparts/cardbase/Cardbase.java b/src/eu/equalparts/cardbase/Cardbase.java index d69dee3..04a2760 100644 --- a/src/eu/equalparts/cardbase/Cardbase.java +++ b/src/eu/equalparts/cardbase/Cardbase.java @@ -14,11 +14,12 @@ import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonMappingException; +import eu.equalparts.cardbase.cards.Card; import eu.equalparts.cardbase.comparator.CardComparator; -import eu.equalparts.cardbase.data.Card; -import eu.equalparts.cardbase.data.ReferenceDeck; -import eu.equalparts.cardbase.data.StandaloneDeck; +import eu.equalparts.cardbase.decks.ReferenceDeck; +import eu.equalparts.cardbase.decks.StandaloneDeck; import eu.equalparts.cardbase.utils.JSON; +import eu.equalparts.cardbase.utils.UID; /** * Provides a variety of utility methods to interact with an optionally loaded cardbase. @@ -40,11 +41,7 @@ public class Cardbase { * Debug flag is raised when the DEBUG environment variable is set. This causes additional * information to be printed to the console. */ - public static final boolean DEBUG = System.getenv("CB_DEBUG") != null; - /** - * Used in the hash generation. - */ - private static final String HASH_DIVIDER = "~"; + public static final boolean DEBUG = System.getenv("CB_DEBUG") != null; /** * Initialises the cardbase with the contents of a file. @@ -95,7 +92,7 @@ public class Cardbase { card.count += count; } else { cardToAdd.count = count; - cards.put(makeHash(cardToAdd), cardToAdd); + cards.put(UID.makeHash(cardToAdd), cardToAdd); } } @@ -120,7 +117,7 @@ public class Cardbase { Integer removed = 0; if (card != null) { if (card.count <= count) { - cards.remove(makeHash(card)); + cards.remove(UID.makeHash(card)); removed = card.count; } else { card.count -= count; @@ -162,7 +159,7 @@ public class Cardbase { * @return the requested {@code Card} or null if no card is found. */ public Card getCard(String setCode, String number) { - return cards.get(makeHash(setCode, number)); + return cards.get(UID.makeHash(setCode, number)); } /** @@ -177,27 +174,6 @@ public class Cardbase { return cards.get(hash); } - /** - * Generate the hash used as a key in the storage map. - * - * @param setCode the card's set code. - * @param number the card's set number. - * @return the generated hash. - */ - public static String makeHash(String setCode, String number) { - return setCode + HASH_DIVIDER + number; - } - - /** - * Generate the hash used as a key in the storage map. - * - * @param the {@code Card} whose hash is desired. - * @return the generated hash. - */ - public static String makeHash(Card card) { - return card.setCode + HASH_DIVIDER + card.number; - } - public Map<String, ReferenceDeck> getDecks() { return Collections.unmodifiableMap(decks); } @@ -205,7 +181,7 @@ public class Cardbase { public List<Card> getMissingCards(StandaloneDeck deckToCheck) { List<Card> missingCards = new ArrayList<Card>(); for (Card card : deckToCheck.cards) { - String hash = makeHash(card); + String hash = UID.makeHash(card); if (cards.containsKey(hash)) { if (cards.get(hash).count < card.count) { Card missingCard = card.clone(); diff --git a/src/eu/equalparts/cardbase/data/Card.java b/src/eu/equalparts/cardbase/cards/Card.java index 8ac5cd1..6afdff8 100644 --- a/src/eu/equalparts/cardbase/data/Card.java +++ b/src/eu/equalparts/cardbase/cards/Card.java @@ -1,4 +1,4 @@ -package eu.equalparts.cardbase.data; +package eu.equalparts.cardbase.cards; import eu.equalparts.cardbase.comparator.SpecialFields.DirtyNumber; import eu.equalparts.cardbase.comparator.SpecialFields.Rarity; diff --git a/src/eu/equalparts/cardbase/data/CardSetInformation.java b/src/eu/equalparts/cardbase/cards/CardSetInformation.java index d3394b7..587c99a 100644 --- a/src/eu/equalparts/cardbase/data/CardSetInformation.java +++ b/src/eu/equalparts/cardbase/cards/CardSetInformation.java @@ -1,4 +1,4 @@ -package eu.equalparts.cardbase.data; +package eu.equalparts.cardbase.cards; public class CardSetInformation { diff --git a/src/eu/equalparts/cardbase/data/FullCardSet.java b/src/eu/equalparts/cardbase/cards/FullCardSet.java index a7f3ea5..d627111 100644 --- a/src/eu/equalparts/cardbase/data/FullCardSet.java +++ b/src/eu/equalparts/cardbase/cards/FullCardSet.java @@ -1,4 +1,4 @@ -package eu.equalparts.cardbase.data; +package eu.equalparts.cardbase.cards; import java.util.Map; diff --git a/src/eu/equalparts/cardbase/cli/CardbaseCLI.java b/src/eu/equalparts/cardbase/cli/CardbaseCLI.java index d67a198..4d72d5b 100644 --- a/src/eu/equalparts/cardbase/cli/CardbaseCLI.java +++ b/src/eu/equalparts/cardbase/cli/CardbaseCLI.java @@ -13,9 +13,9 @@ import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.databind.JsonMappingException; import eu.equalparts.cardbase.Cardbase; -import eu.equalparts.cardbase.data.Card; -import eu.equalparts.cardbase.data.FullCardSet; -import eu.equalparts.cardbase.data.CardSetInformation; +import eu.equalparts.cardbase.cards.Card; +import eu.equalparts.cardbase.cards.CardSetInformation; +import eu.equalparts.cardbase.cards.FullCardSet; import eu.equalparts.cardbase.utils.MTGUniverse; /** diff --git a/src/eu/equalparts/cardbase/comparator/CardComparator.java b/src/eu/equalparts/cardbase/comparator/CardComparator.java index fe440f3..9cb72b4 100644 --- a/src/eu/equalparts/cardbase/comparator/CardComparator.java +++ b/src/eu/equalparts/cardbase/comparator/CardComparator.java @@ -8,9 +8,9 @@ import java.util.Comparator; import java.util.function.BiFunction; import eu.equalparts.cardbase.Cardbase; +import eu.equalparts.cardbase.cards.Card; import eu.equalparts.cardbase.comparator.SpecialFields.DirtyNumber; import eu.equalparts.cardbase.comparator.SpecialFields.Rarity; -import eu.equalparts.cardbase.data.Card; /** * I'm new to this reflection business, so bear with me. diff --git a/src/eu/equalparts/cardbase/data/Deck.java b/src/eu/equalparts/cardbase/decks/Deck.java index 6a6658a..1f28d60 100644 --- a/src/eu/equalparts/cardbase/data/Deck.java +++ b/src/eu/equalparts/cardbase/decks/Deck.java @@ -1,4 +1,4 @@ -package eu.equalparts.cardbase.data; +package eu.equalparts.cardbase.decks; public abstract class Deck { diff --git a/src/eu/equalparts/cardbase/data/ReferenceDeck.java b/src/eu/equalparts/cardbase/decks/ReferenceDeck.java index 7cdc15f..9e8695b 100644 --- a/src/eu/equalparts/cardbase/data/ReferenceDeck.java +++ b/src/eu/equalparts/cardbase/decks/ReferenceDeck.java @@ -1,9 +1,10 @@ -package eu.equalparts.cardbase.data; +package eu.equalparts.cardbase.decks; import java.util.HashMap; import java.util.Map; -import eu.equalparts.cardbase.Cardbase; +import eu.equalparts.cardbase.cards.Card; +import eu.equalparts.cardbase.utils.UID; public class ReferenceDeck extends Deck { @@ -22,7 +23,7 @@ public class ReferenceDeck extends Deck { this.forests = deck.forests; for (Card card : deck.cards) { - cardReferences.put(Cardbase.makeHash(card), card.count); + cardReferences.put(UID.makeHash(card), card.count); } } diff --git a/src/eu/equalparts/cardbase/data/StandaloneDeck.java b/src/eu/equalparts/cardbase/decks/StandaloneDeck.java index bfb7b55..4c6d25c 100644 --- a/src/eu/equalparts/cardbase/data/StandaloneDeck.java +++ b/src/eu/equalparts/cardbase/decks/StandaloneDeck.java @@ -1,8 +1,10 @@ -package eu.equalparts.cardbase.data; +package eu.equalparts.cardbase.decks; import java.util.HashSet; import java.util.Set; +import eu.equalparts.cardbase.cards.Card; + public class StandaloneDeck extends Deck { public Set<Card> cards = new HashSet<Card>(); diff --git a/src/eu/equalparts/cardbase/decks/Statistics.java b/src/eu/equalparts/cardbase/decks/Statistics.java new file mode 100644 index 0000000..9efc82a --- /dev/null +++ b/src/eu/equalparts/cardbase/decks/Statistics.java @@ -0,0 +1,80 @@ +package eu.equalparts.cardbase.decks; + +import eu.equalparts.cardbase.cards.Card; + +public final class Statistics { + + private Statistics() {} + + + + public static double calculatePercentage(StandaloneDeck deck, String type) { + double allCardsByType = count(deck, type); + double allCards = count(deck); + return allCardsByType / allCards; + } + + public static int count(StandaloneDeck deck, String type) { + int count = type.contains("Land") ? countBasicLands(deck) : 0; + for (Card card : deck.cards) { + if (card.type != null && + card.type.contains(type)) { + count += card.count; + } + } + return count; + } + + public static int count(StandaloneDeck deck) { + int totalCards = countBasicLands(deck); + + for (Card card : deck.cards) { + totalCards += card.count; + } + + return totalCards; + } + + private static int countBasicLands(StandaloneDeck deck) { + return deck.plains + + deck.islands + + deck.swamps + + deck.mountains + + deck.forests; + } + + public static int[] computeDistribution(StandaloneDeck deck, String type) { + int arraySize = 0; + for (Card card : deck.cards) { + if (card.type != null && card.type.contains(type)) + if (card.cmc != null && card.cmc >= arraySize) + arraySize = card.cmc + 1; + } + + int[] costs = new int[arraySize]; + for (Card card : deck.cards) { + if (card.type != null && card.type.contains(type)) + if (card.cmc != null) + costs[card.cmc] += card.count; + } + + return costs; + } + + public static int[] computeDistribution(StandaloneDeck deck) { + int arraySize = 0; + for (Card card : deck.cards) { + if (card.cmc != null && card.cmc >= arraySize) + arraySize = card.cmc + 1; + } + + int[] costs = new int[arraySize]; + for (Card card : deck.cards) { + if (card.cmc != null) + costs[card.cmc] += card.count; + } + + return costs; + } + +} diff --git a/src/eu/equalparts/cardbase/gui/CardView.java b/src/eu/equalparts/cardbase/gui/CardView.java index 61504ee..2ec7576 100644 --- a/src/eu/equalparts/cardbase/gui/CardView.java +++ b/src/eu/equalparts/cardbase/gui/CardView.java @@ -1,6 +1,6 @@ package eu.equalparts.cardbase.gui; -import eu.equalparts.cardbase.data.Card; +import eu.equalparts.cardbase.cards.Card; import javafx.event.EventHandler; import javafx.scene.image.ImageView; import javafx.scene.input.MouseEvent; diff --git a/src/eu/equalparts/cardbase/utils/MTGUniverse.java b/src/eu/equalparts/cardbase/utils/MTGUniverse.java index b52c3e6..1e8efc2 100644 --- a/src/eu/equalparts/cardbase/utils/MTGUniverse.java +++ b/src/eu/equalparts/cardbase/utils/MTGUniverse.java @@ -12,9 +12,9 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.JsonNode; -import eu.equalparts.cardbase.data.Card; -import eu.equalparts.cardbase.data.CardSetInformation; -import eu.equalparts.cardbase.data.FullCardSet; +import eu.equalparts.cardbase.cards.Card; +import eu.equalparts.cardbase.cards.CardSetInformation; +import eu.equalparts.cardbase.cards.FullCardSet; /** * Access point to the complete set of cards that exist in the diff --git a/src/eu/equalparts/cardbase/utils/UID.java b/src/eu/equalparts/cardbase/utils/UID.java new file mode 100644 index 0000000..ae4a11d --- /dev/null +++ b/src/eu/equalparts/cardbase/utils/UID.java @@ -0,0 +1,33 @@ +package eu.equalparts.cardbase.utils; + +import eu.equalparts.cardbase.cards.Card; + +public class UID { + + /** + * Used in the hash generation. + */ + private static final String HASH_DIVIDER = "~"; + + /** + * Generate the hash used as a key in the storage map. + * + * @param setCode the card's set code. + * @param number the card's set number. + * @return the generated hash. + */ + public static String makeHash(String setCode, String number) { + return setCode + HASH_DIVIDER + number; + } + + /** + * Generate the hash used as a key in the storage map. + * + * @param the {@code Card} whose hash is desired. + * @return the generated hash. + */ + public static String makeHash(Card card) { + return card.setCode + HASH_DIVIDER + card.number; + } + +} diff --git a/test/Testest.java b/test/Testest.java index 7128b75..855568d 100644 --- a/test/Testest.java +++ b/test/Testest.java @@ -7,15 +7,15 @@ import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.databind.JsonMappingException; import eu.equalparts.cardbase.Cardbase; -import eu.equalparts.cardbase.data.Card; -import eu.equalparts.cardbase.data.StandaloneDeck; +import eu.equalparts.cardbase.cards.Card; +import eu.equalparts.cardbase.decks.StandaloneDeck; import eu.equalparts.cardbase.utils.JSON; public class Testest { public static void main(String... args) throws JsonParseException, JsonMappingException, MalformedURLException, IOException { - StandaloneDeck deck = JSON.mapper.readValue(new File("control.cbd"), StandaloneDeck.class); + StandaloneDeck deck = JSON.mapper.readValue(new File("deck.cbd"), StandaloneDeck.class); System.out.println(deck.name); System.out.println("plains: " + deck.plains); @@ -24,15 +24,11 @@ public class Testest { System.out.println("mountains: " + deck.mountains); System.out.println("forests: " + deck.forests); + int count = 0; for (Card card : deck.cards) { - System.out.println(card.count + "x " + card.name); - } - - Cardbase cards = new Cardbase(new File("cards.cb")); - List<Card> missingCards = cards.getMissingCards(deck); - - for (Card card : missingCards) { - System.out.println(card.count + "x " + card.name); + if (card.type != null && card.type.contains("Instant")) + System.out.println(card.count + "x " + card.cmc + " (" + card.name + ")"); } + System.out.println("total: " + count); } } diff --git a/test/eu/equalparts/test/cardbase/CardbaseSortTest.java b/test/eu/equalparts/test/cardbase/CardbaseSortTest.java index 2e756a3..81cb6ef 100644 --- a/test/eu/equalparts/test/cardbase/CardbaseSortTest.java +++ b/test/eu/equalparts/test/cardbase/CardbaseSortTest.java @@ -13,7 +13,7 @@ import org.junit.Test; import com.fasterxml.jackson.core.type.TypeReference; import eu.equalparts.cardbase.Cardbase; -import eu.equalparts.cardbase.data.Card; +import eu.equalparts.cardbase.cards.Card; import eu.equalparts.cardbase.utils.JSON; /** diff --git a/test/eu/equalparts/test/decks/DeckTest.java b/test/eu/equalparts/test/decks/DeckTest.java index 32b1a54..c6bf0a1 100644 --- a/test/eu/equalparts/test/decks/DeckTest.java +++ b/test/eu/equalparts/test/decks/DeckTest.java @@ -7,11 +7,11 @@ import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; -import eu.equalparts.cardbase.Cardbase; -import eu.equalparts.cardbase.data.Card; -import eu.equalparts.cardbase.data.ReferenceDeck; -import eu.equalparts.cardbase.data.StandaloneDeck; +import eu.equalparts.cardbase.cards.Card; +import eu.equalparts.cardbase.decks.ReferenceDeck; +import eu.equalparts.cardbase.decks.StandaloneDeck; import eu.equalparts.cardbase.utils.JSON; +import eu.equalparts.cardbase.utils.UID; public class DeckTest { @@ -40,13 +40,10 @@ public class DeckTest { referenceDeck.swamps == standaloneDeck.swamps && referenceDeck.mountains == standaloneDeck.mountains && referenceDeck.forests == standaloneDeck.forests; - assertTrue("Metadata was not correctly set.", condition); assertEquals("Wrong number of cards.", referenceDeck.cardReferences.size(), standaloneDeck.cards.size()); - for (Card card : standaloneDeck.cards) { - System.out.println("Checking card: " + card.name); - Integer count = referenceDeck.cardReferences.get(Cardbase.makeHash(card)); + Integer count = referenceDeck.cardReferences.get(UID.makeHash(card)); assertNotNull("Reference missing in deck.", count); assertEquals("Card count is wrong.", card.count, count); } diff --git a/test/eu/equalparts/test/decks/StatisticsTest.java b/test/eu/equalparts/test/decks/StatisticsTest.java new file mode 100644 index 0000000..41eef0a --- /dev/null +++ b/test/eu/equalparts/test/decks/StatisticsTest.java @@ -0,0 +1,146 @@ +package eu.equalparts.test.decks; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import eu.equalparts.cardbase.decks.StandaloneDeck; +import eu.equalparts.cardbase.decks.Statistics; +import eu.equalparts.cardbase.utils.JSON; + +public class StatisticsTest { + + private static StandaloneDeck testDeck; + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + testDeck = JSON.mapper.readValue(StatisticsTest.class.getResourceAsStream("deck.cbd"), StandaloneDeck.class); + } + + @AfterClass + public static void tearDownAfterClass() throws Exception { + } + + @Before + public void setUp() throws Exception { + } + + @Test + public void test_totalLandCountIsComputedCorrectly() throws Exception { + int count = Statistics.count(testDeck, "Land"); + + assertEquals(23, count); + } + + @Test + public void test_basicLandCountIsComputedCorrectly() throws Exception { + int count = Statistics.count(testDeck, "Basic Land"); + + assertEquals(20, count); + } + + @Test + public void test_cardCountIsComputedCorrectly() throws Exception { + int count = Statistics.count(testDeck); + + assertEquals(60, count); + } + + @Test + public void test_landPercentageIsComputedCorrectly() throws Exception { + double percentage = Statistics.calculatePercentage(testDeck, "Land"); + + assertTrue("Land percentage should be " + (23.0 / 60.0) + ", is " + percentage, percentage == (23.0 / 60.0)); + } + + @Test + public void test_creaturePercentageIsComputedCorrectly() throws Exception { + double percentage = Statistics.calculatePercentage(testDeck, "Creature"); + + assertTrue("Creature percentage should be " + (24.0 / 60.0) + ", is " + percentage, percentage == (24.0 / 60.0)); + } + + @Test + public void test_creatureCountIsComputedCorrectly() throws Exception { + int count = Statistics.count(testDeck, "Creature"); + + assertEquals(24, count); + } + + @Test + public void test_sorceryCountIsComputedCorrectly() throws Exception { + int count = Statistics.count(testDeck, "Sorcery"); + + assertEquals(1, count); + } + + @Test + public void test_instantCountIsComputedCorrectly() throws Exception { + int count = Statistics.count(testDeck, "Instant"); + + assertEquals(6, count); + } + + @Test + public void test_planeswalkerCountIsComputedCorrectly() throws Exception { + int count = Statistics.count(testDeck, "Planeswalker"); + + assertEquals(0, count); + } + + @Test + public void test_elfCountIsComputedCorrectly() throws Exception { + int count = Statistics.count(testDeck, "Elf"); + + assertEquals(2, count); + } + + @Test + public void test_overallCostDistributionIsComputedCorrectly() throws Exception { + int[] actualCosts = Statistics.computeDistribution(testDeck); + int[] expectedCosts = {0, 8, 11, 3, 4, 7, 4}; + + assertEquals("Array lengths do not match.", expectedCosts.length, actualCosts.length); + for (int i = 0; i < expectedCosts.length; i++) { + assertEquals("CMC: " + i, expectedCosts[i], actualCosts[i]); + } + } + + @Test + public void test_creatureCostDistributionIsComputedCorrectly() throws Exception { + int[] actualCosts = Statistics.computeDistribution(testDeck, "Creature"); + int[] expectedCosts = {0, 3, 6, 2, 2, 7, 4}; + + assertEquals("Array lengths do not match.", expectedCosts.length, actualCosts.length); + for (int i = 0; i < expectedCosts.length; i++) { + assertEquals("CMC: " + i, expectedCosts[i], actualCosts[i]); + } + } + + @Test + public void test_instantCostDistributionIsComputedCorrectly() throws Exception { + int[] actualCosts = Statistics.computeDistribution(testDeck, "Instant"); + int[] expectedCosts = {0, 2, 4}; + + assertEquals("Array lengths do not match.", expectedCosts.length, actualCosts.length); + for (int i = 0; i < expectedCosts.length; i++) { + assertEquals("CMC: " + i, expectedCosts[i], actualCosts[i]); + } + } + + @Test + public void test_planeswalkerCostDistributionIsComputedCorrectly() throws Exception { + int[] actualCosts = Statistics.computeDistribution(testDeck, "Planeswalker"); + int[] expectedCosts = {}; + + assertEquals("Array lengths do not match.", expectedCosts.length, actualCosts.length); + for (int i = 0; i < expectedCosts.length; i++) { + assertEquals("CMC: " + i, expectedCosts[i], actualCosts[i]); + } + } + +}
\ No newline at end of file |