aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/eu/equalparts/cardbase/CardEntry.java25
-rw-r--r--src/eu/equalparts/cardbase/Cardbase.java19
-rw-r--r--src/eu/equalparts/cardbase/cli/CardbaseCLI.java6
-rw-r--r--test/eu/equalparts/cardbase/CardbaseSortTest.java11
-rw-r--r--test/eu/equalparts/cardbase/CardbaseTest.java51
-rw-r--r--test/shivandragon.json2
-rw-r--r--test/testbase.cb2
-rw-r--r--test/testcards.json2
-rw-r--r--todo5
9 files changed, 51 insertions, 72 deletions
diff --git a/src/eu/equalparts/cardbase/CardEntry.java b/src/eu/equalparts/cardbase/CardEntry.java
deleted file mode 100644
index c4a4c94..0000000
--- a/src/eu/equalparts/cardbase/CardEntry.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package eu.equalparts.cardbase;
-
-import eu.equalparts.cardbase.cards.Card;
-
-/**
- * The purpose of this class is to hold a single {@code Card} object but
- * also all associated metadata, such as count.
- *
- * @author Eduardo Pedroni
- *
- */
-public class CardEntry {
-
- private final Card card;
- public int count;
-
- public CardEntry(Card card, int count) {
- this.card = card;
- this.count = count;
- }
-
- public Card card() {
- return card;
- }
-}
diff --git a/src/eu/equalparts/cardbase/Cardbase.java b/src/eu/equalparts/cardbase/Cardbase.java
index af8e7d5..075f7f3 100644
--- a/src/eu/equalparts/cardbase/Cardbase.java
+++ b/src/eu/equalparts/cardbase/Cardbase.java
@@ -11,8 +11,8 @@ import java.util.Map;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonParseException;
-import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.JsonNode;
import eu.equalparts.cardbase.cards.Card;
import eu.equalparts.cardbase.comparator.CardComparator;
@@ -56,14 +56,21 @@ public class Cardbase {
* @throws IOException if a low-level I/O problem (unexpected end-of-input, network error) occurs.
*/
public Cardbase(File cardbaseFile) throws JsonParseException, JsonMappingException, IOException {
- cards = JSON.mapper.readValue(cardbaseFile, new TypeReference<Map<Integer, Card>>() {});
+ initialise();
+ parseCardbase(JSON.mapper.readValue(cardbaseFile, JsonNode.class));
+
}
/**
* Initialises a clean cardbase.
*/
public Cardbase() {
+ initialise();
+ }
+
+ private void initialise() {
cards = new HashMap<Integer, Card>();
+ collection = new HashMap<Integer, Integer>();
decks = new HashMap<String, ReferenceDeck>();
}
@@ -192,11 +199,15 @@ public class Cardbase {
return Collections.unmodifiableMap(decks);
}
- public int getCount(String setCode, String number) {
- Integer count = collection.get(Card.makeHash(setCode, number));
+ public int getCount(Card card) {
+ Integer count = collection.get(Card.makeHash(card.setCode, card.number));
return count != null ? count : 0;
}
+ private void parseCardbase(JsonNode jsonTree) {
+
+ }
+
// public List<Card> getMissingCards(StandaloneDeck deckToCheck) {
// List<Card> missingCards = new ArrayList<Card>();
// for (Card card : deckToCheck.cards) {
diff --git a/src/eu/equalparts/cardbase/cli/CardbaseCLI.java b/src/eu/equalparts/cardbase/cli/CardbaseCLI.java
index 8aece5d..b1a4028 100644
--- a/src/eu/equalparts/cardbase/cli/CardbaseCLI.java
+++ b/src/eu/equalparts/cardbase/cli/CardbaseCLI.java
@@ -337,7 +337,7 @@ public final class CardbaseCLI {
int total = 0;
for (Card card : cardbase.getCards()) {
printGlance(card);
- total += cardbase.getCount(card.setCode, card.number);
+ total += cardbase.getCount(card);
}
System.out.println("Total: " + total);
}
@@ -365,7 +365,7 @@ public final class CardbaseCLI {
int total = 0;
for (Card card : cardbase.getCards()) {
printPerusal(card);
- total += cardbase.getCount(card.setCode, card.number);
+ total += cardbase.getCount(card);
}
System.out.println("Total: " + total);
}
@@ -523,7 +523,7 @@ public final class CardbaseCLI {
* @param card the card to glance.
*/
private void printGlance(Card card) {
- System.out.println(String.format("%1$-4d %2$s (%3$s, %4$s)", cardbase.getCount(card.setCode, card.number), card.name, card.setCode, card.number));
+ System.out.println(String.format("%1$-4d %2$s (%3$s, %4$s)", cardbase.getCount(card), card.name, card.setCode, card.number));
}
/**
diff --git a/test/eu/equalparts/cardbase/CardbaseSortTest.java b/test/eu/equalparts/cardbase/CardbaseSortTest.java
index e3a92ee..2dbb68e 100644
--- a/test/eu/equalparts/cardbase/CardbaseSortTest.java
+++ b/test/eu/equalparts/cardbase/CardbaseSortTest.java
@@ -1,5 +1,6 @@
package eu.equalparts.cardbase;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.Collection;
@@ -40,8 +41,9 @@ public class CardbaseSortTest {
@Before
public void setUp() throws Exception {
uut = new Cardbase();
- for (Card card : testCards) {
- uut.addCard(card, card.count);
+ int[] cardCounts = {1, 2, 3, 8, 1, 15, 1, 1};
+ for (int i = 0; i < testCards.size(); i++) {
+ uut.addCard(testCards.get(i), cardCounts[i]);
}
}
@@ -387,11 +389,10 @@ public class CardbaseSortTest {
public void sortByCount() throws Exception {
Collection<Card> sortedCards = uut.sort("count");
int i = 0;
- Integer[] counts = {1, 1, 1, 1, 2, 3, 8, 15 };
+ int[] counts = {1, 1, 1, 1, 2, 3, 8, 15 };
for (Card card : sortedCards) {
- assertTrue(card.count + " should have been " + counts[i] + ", i = " + i, card.count.equals(counts[i]));
+ assertEquals(uut.getCount(card) + " should have been " + counts[i] + ", i = " + i, uut.getCount(card), counts[i]);
i++;
}
}
-
}
diff --git a/test/eu/equalparts/cardbase/CardbaseTest.java b/test/eu/equalparts/cardbase/CardbaseTest.java
index 424befc..ec9494e 100644
--- a/test/eu/equalparts/cardbase/CardbaseTest.java
+++ b/test/eu/equalparts/cardbase/CardbaseTest.java
@@ -90,7 +90,7 @@ public class CardbaseTest {
for (CardInfo ci : testCards) {
Card card = uut.getCard(ci.setCode, ci.number);
assertNotNull("Missing card, set " + ci.setCode + ", " + ci.number, card);
- assertEquals("Wrong card count, set " + ci.setCode + ", " + ci.number, ci.count, card.count);
+ assertEquals("Wrong card count, set " + ci.setCode + ", " + ci.number, ci.count, (Integer) uut.getCount(card));
}
}
@@ -139,19 +139,21 @@ public class CardbaseTest {
***********************************************************************************/
@Test
public void cardbaseIsSaved() throws Exception {
+ final int testCount = 5;
+
File testFile = tempFolder.newFile("saveTest.cb");
uut.writeCollection(testFile);
uut = new Cardbase(testFile);
assertEquals("Cardbase should contain no cards.", 0, uut.getCards().size());
- uut.addCard(testCard, testCard.count);
+ uut.addCard(testCard, testCount);
uut.writeCollection(testFile);
uut = new Cardbase(testFile);
- assertEquals("Cardbase should contain one card.", 1, uut.getCards().size());
+ assertEquals("Cardbase should contain " + testCount + " cards.", testCount, uut.getCards().size());
Card card = uut.getCard("M15", "281");
assertNotNull("Cardbase should contain a Shivan Dragon.", card);
- assertEquals("Cardbase should contain only one Shivan Dragon.", new Integer(1), card.count);
+ assertEquals("Cardbase should contain " + testCount + " Shivan Dragon.", testCount, uut.getCount(card));
}
/*
@@ -176,7 +178,7 @@ public class CardbaseTest {
***********************************************************************************/
@Test
public void newCardIsAdded() throws Exception {
- uut.addCard(testCard, testCard.count);
+ uut.addCard(testCard, 1);
Card addedCard = uut.getCard("M15", "281");
assertNotNull("Card was not found in cardbase.", addedCard);
@@ -185,15 +187,12 @@ public class CardbaseTest {
@Test
public void existingCardIsIncremented() throws Exception {
- Card shivanClone = testCard.clone();
- uut.addCard(shivanClone, shivanClone.count);
- Card shivanClone2 = testCard.clone();
- shivanClone2.count = 2;
- uut.addCard(shivanClone2, shivanClone2.count);
+ uut.addCard(testCard, 2);
+ uut.addCard(testCard, 4);
Card addedCard = uut.getCard("M15", "281");
assertNotNull("Card was not found in cardbase.", addedCard);
- assertEquals("Card count was not updated correctly.", new Integer(3), addedCard.count);
+ assertEquals("Card count was not updated correctly.", 6, uut.getCount(addedCard));
}
/*
@@ -210,39 +209,31 @@ public class CardbaseTest {
***********************************************************************************/
@Test
public void cardRemoveCountIsLessThanCardCount() throws Exception {
- Card shivanClone = testCard.clone();
- shivanClone.count = 5;
- uut.addCard(shivanClone, shivanClone.count);
+ uut.addCard(testCard, 5);
- int removed = uut.removeCard(testCard, testCard.count);
+ int removed = uut.removeCard(testCard, 3);
Card removedCard = uut.getCard("M15", "281");
assertNotNull("Card was not found in cardbase.", removedCard);
- assertEquals("Card count was not updated correctly.", new Integer(4), removedCard.count);
- assertEquals("Cardbase reports wrong removed count.", 1, removed);
+ assertEquals("Card count was not updated correctly.", 2, uut.getCount(removedCard));
+ assertEquals("Cardbase reports wrong removed count.", 3, removed);
}
@Test
public void cardRemoveCountIsEqualToCardCount() throws Exception {
- Card shivanClone = testCard.clone();
- uut.addCard(shivanClone, shivanClone.count);
+ uut.addCard(testCard, 5);
- int removed = uut.removeCard(testCard, testCard.count);
+ int removed = uut.removeCard(testCard, 5);
Card removedCard = uut.getCard("M15", "281");
assertNull("Card was not removed from cardbase.", removedCard);
- assertEquals("Cardbase reports wrong removed count.", 1, removed);
+ assertEquals("Cardbase reports wrong removed count.", 5, removed);
}
@Test
public void cardRemoveCountIsGreaterThanCardCount() throws Exception {
- Card shivanClone = testCard.clone();
- shivanClone.count = 3;
- uut.addCard(shivanClone, shivanClone.count);
-
- Card shivanClone2 = testCard.clone();
- shivanClone2.count = 5;
- int removed = uut.removeCard(shivanClone2, shivanClone2.count);
+ uut.addCard(testCard, 3);
+ int removed = uut.removeCard(testCard, 5);
Card removedCard = uut.getCard("M15", "281");
assertNull("Card was not removed from cardbase.", removedCard);
@@ -260,7 +251,7 @@ public class CardbaseTest {
@Test
public void removedCardIsNotInCardbase() throws Exception {
- int removed = uut.removeCard(testCard, testCard.count);
+ int removed = uut.removeCard(testCard, 1);
assertEquals("Removed count should be 0.", 0, removed);
}
@@ -270,7 +261,7 @@ public class CardbaseTest {
***********************************************************************************/
@Test
public void correctCardIsReturnedByGetter() throws Exception {
- uut.addCard(testCard.clone(), testCard.count);
+ uut.addCard(testCard, 1);
Card card = uut.getCard("M15", "281");
diff --git a/test/shivandragon.json b/test/shivandragon.json
index 345d4b1..6237c3c 100644
--- a/test/shivandragon.json
+++ b/test/shivandragon.json
@@ -1 +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","watermark":null,"setCode":"M15","imageCode":"m15","count":1} \ No newline at end of file
+{"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","watermark":null,"setCode":"M15","imageCode":"m15"}
diff --git a/test/testbase.cb b/test/testbase.cb
index 80dc98f..13eee5d 100644
--- a/test/testbase.cb
+++ b/test/testbase.cb
@@ -1 +1 @@
-{"1588769":{"name":"Reverberate","layout":"normal","manaCost":"{R}{R}","cmc":2,"type":"Instant","rarity":"Rare","text":"Copy target instant or sorcery spell. You may choose new targets for the copy.","flavor":"For every action, there is a swifter and more violent reaction.","artist":"jD","number":"152","power":null,"toughness":null,"loyalty":null,"multiverseid":233722,"imageName":"reverberate","watermark":null,"setCode":"M12","imageCode":"m12","count":1},"126251":{"name":"Mighty Leap","layout":"normal","manaCost":"{1}{W}","cmc":2,"type":"Instant","rarity":"Common","text":"Target creature gets +2/+2 and gains flying until end of turn. (It can't be blocked except by creatures with flying or reach.)","flavor":"\"The southern fortress taken by invaders? Heh, sure . . . when elephants fly.\"\n—Brezard Skeinbow, captain of the guard","artist":"rk post","number":"26","power":null,"toughness":null,"loyalty":null,"multiverseid":241989,"imageName":"mighty leap","watermark":null,"setCode":"M12","imageCode":"m12","count":1},"1580419":{"name":"Formless Nurturing","layout":"normal","manaCost":"{3}{G}","cmc":4,"type":"Sorcery","rarity":"Common","text":"Manifest the top card of your library, then put a +1/+1 counter on it. (To manifest a card, put it onto the battlefield face down as a 2/2 creature. Turn it face up any time for its mana cost if it's a creature card.)","flavor":null,"artist":"Cliff Childs","number":"129","power":null,"toughness":null,"loyalty":null,"multiverseid":391837,"imageName":"formless nurturing","watermark":null,"setCode":"FRF","imageCode":"frf","count":8},"1580388":{"name":"Feral Krushok","layout":"normal","manaCost":"{4}{G}","cmc":5,"type":"Creature — Beast","rarity":"Common","text":null,"flavor":"In a stunning act of diplomacy, Yasova Dragonclaw ceded a portion of Temur lands to the Sultai. Her clan protested until they saw she had given the Sultai the breeding grounds of the krushoks. They hadn't realized she had a sense of humor.","artist":"Kev Walker","number":"128","power":"5","toughness":"4","loyalty":null,"multiverseid":391831,"imageName":"feral krushok","watermark":null,"setCode":"FRF","imageCode":"frf","count":1},"1580357":{"name":"Destructor Dragon","layout":"normal","manaCost":"{4}{G}{G}","cmc":6,"type":"Creature — Dragon","rarity":"Uncommon","text":"Flying\nWhen Destructor Dragon dies, destroy target noncreature permanent.","flavor":"After countless attacks on the Salt Road where it passes through the frozen tundra, the Abzan began to refer to the area as Atarka territory rather than Temur lands.","artist":"Peter Mohrbacher","number":"127","power":"4","toughness":"4","loyalty":null,"multiverseid":391818,"imageName":"destructor dragon","watermark":"Atarka","setCode":"FRF","imageCode":"frf","count":1},"127150":{"name":"Siege Mastodon","layout":"normal","manaCost":"{4}{W}","cmc":5,"type":"Creature — Elephant","rarity":"Common","text":null,"flavor":"\"The walls of the wicked will fall before us. Ready the siege engines. We proceed to war!\"\n—General Avitora","artist":"Matt Cavotta","number":"34","power":"3","toughness":"5","loyalty":null,"multiverseid":222635,"imageName":"siege mastodon","watermark":null,"setCode":"M12","imageCode":"m12","count":2}} \ No newline at end of file
+{"1588769":{"name":"Reverberate","layout":"normal","manaCost":"{R}{R}","cmc":2,"type":"Instant","rarity":"Rare","text":"Copy target instant or sorcery spell. You may choose new targets for the copy.","flavor":"For every action, there is a swifter and more violent reaction.","artist":"jD","number":"152","power":null,"toughness":null,"loyalty":null,"multiverseid":233722,"imageName":"reverberate","watermark":null,"setCode":"M12","imageCode":"m12"},"126251":{"name":"Mighty Leap","layout":"normal","manaCost":"{1}{W}","cmc":2,"type":"Instant","rarity":"Common","text":"Target creature gets +2/+2 and gains flying until end of turn. (It can't be blocked except by creatures with flying or reach.)","flavor":"\"The southern fortress taken by invaders? Heh, sure . . . when elephants fly.\"\n—Brezard Skeinbow, captain of the guard","artist":"rk post","number":"26","power":null,"toughness":null,"loyalty":null,"multiverseid":241989,"imageName":"mighty leap","watermark":null,"setCode":"M12","imageCode":"m12"},"1580419":{"name":"Formless Nurturing","layout":"normal","manaCost":"{3}{G}","cmc":4,"type":"Sorcery","rarity":"Common","text":"Manifest the top card of your library, then put a +1/+1 counter on it. (To manifest a card, put it onto the battlefield face down as a 2/2 creature. Turn it face up any time for its mana cost if it's a creature card.)","flavor":null,"artist":"Cliff Childs","number":"129","power":null,"toughness":null,"loyalty":null,"multiverseid":391837,"imageName":"formless nurturing","watermark":null,"setCode":"FRF","imageCode":"frf"},"1580388":{"name":"Feral Krushok","layout":"normal","manaCost":"{4}{G}","cmc":5,"type":"Creature — Beast","rarity":"Common","text":null,"flavor":"In a stunning act of diplomacy, Yasova Dragonclaw ceded a portion of Temur lands to the Sultai. Her clan protested until they saw she had given the Sultai the breeding grounds of the krushoks. They hadn't realized she had a sense of humor.","artist":"Kev Walker","number":"128","power":"5","toughness":"4","loyalty":null,"multiverseid":391831,"imageName":"feral krushok","watermark":null,"setCode":"FRF","imageCode":"frf"},"1580357":{"name":"Destructor Dragon","layout":"normal","manaCost":"{4}{G}{G}","cmc":6,"type":"Creature — Dragon","rarity":"Uncommon","text":"Flying\nWhen Destructor Dragon dies, destroy target noncreature permanent.","flavor":"After countless attacks on the Salt Road where it passes through the frozen tundra, the Abzan began to refer to the area as Atarka territory rather than Temur lands.","artist":"Peter Mohrbacher","number":"127","power":"4","toughness":"4","loyalty":null,"multiverseid":391818,"imageName":"destructor dragon","watermark":"Atarka","setCode":"FRF","imageCode":"frf"},"127150":{"name":"Siege Mastodon","layout":"normal","manaCost":"{4}{W}","cmc":5,"type":"Creature — Elephant","rarity":"Common","text":null,"flavor":"\"The walls of the wicked will fall before us. Ready the siege engines. We proceed to war!\"\n—General Avitora","artist":"Matt Cavotta","number":"34","power":"3","toughness":"5","loyalty":null,"multiverseid":222635,"imageName":"siege mastodon","watermark":null,"setCode":"M12","imageCode":"m12"}}
diff --git a/test/testcards.json b/test/testcards.json
index 18b3783..3ab9906 100644
--- a/test/testcards.json
+++ b/test/testcards.json
@@ -1 +1 @@
-[{"name":"Callow Jushi","layout":"flip","manaCost":"{1}{U}{U}","cmc":3,"type":"Creature — Human Wizard","rarity":"Uncommon","text":"Whenever you cast a Spirit or Arcane spell, you may put a ki counter on Callow Jushi.\nAt the beginning of the end step, if there are two or more ki counters on Callow Jushi, you may flip it.","flavor":null,"artist":"Tsutomu Kawade","number":"31a","power":"2","toughness":"2","loyalty":null,"multiverseid":74489,"imageName":"callow jushi","watermark":null,"setCode":"BOK","imageCode":"bok","count":1},{"name":"Coerced Confession","layout":"normal","manaCost":"{4}{U/B}","cmc":5,"type":"Sorcery","rarity":"Uncommon","text":"Target player puts the top four cards of his or her library into his or her graveyard. You draw a card for each creature card put into that graveyard this way.","flavor":"\"Ask the right questions in the right way and truth is inevitable.\"\n—Lazav","artist":"Mathias Kollros","number":"217","power":null,"toughness":null,"loyalty":null,"multiverseid":366408,"imageName":"coerced confession","watermark":"Dimir","setCode":"GTC","imageCode":"gtc","count":2},{"name":"Khalni Hydra","layout":"normal","manaCost":"{G}{G}{G}{G}{G}{G}{G}{G}","cmc":8,"type":"Creature — Hydra","rarity":"Mythic Rare","text":"Khalni Hydra costs {G} less to cast for each green creature you control.\nTrample","flavor":"\"In ages past, bargains were struck and promises were made. Now we must collect on our debt. Begin the hymns.\"\n—Moruul, Khalni druid","artist":"Todd Lockwood","number":"192","power":"8","toughness":"8","loyalty":null,"multiverseid":193551,"imageName":"khalni hydra","watermark":null,"setCode":"ROE","imageCode":"roe","count":3},{"name":"Nightmare","layout":"normal","manaCost":"{5}{B}","cmc":6,"type":"Creature — Nightmare Horse","rarity":"Rare","text":"Flying (This creature can't be blocked except by creatures with flying or reach.)\nNightmare's power and toughness are each equal to the number of Swamps you control.","flavor":"The thunder of its hooves beats dreams into despair.","artist":"Vance Kovacs","number":"276","power":"*","toughness":"*","loyalty":null,"multiverseid":383168,"imageName":"nightmare","watermark":null,"setCode":"M15","imageCode":"m15","count":8},{"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","watermark":null,"setCode":"M15","imageCode":"m15","count":1},{"name":"Disrupting Shoal","layout":"normal","manaCost":"{X}{U}{U}","cmc":2,"type":"Instant — Arcane","rarity":"Rare","text":"You may exile a blue card with converted mana cost X from your hand rather than pay Disrupting Shoal's mana cost.\nCounter target spell if its converted mana cost is X.","flavor":null,"artist":"Scott M. Fischer","number":"33","power":null,"toughness":null,"loyalty":null,"multiverseid":74128,"imageName":"disrupting shoal","watermark":null,"setCode":"BOK","imageCode":"bok","count":15},{"name":"Sorin Markov","layout":"normal","manaCost":"{3}{B}{B}{B}","cmc":6,"type":"Planeswalker — Sorin","rarity":"Mythic Rare","text":"+2: Sorin Markov deals 2 damage to target creature or player and you gain 2 life.\n−3: Target opponent's life total becomes 10.\n−7: You control target player during that player's next turn.","flavor":null,"artist":"Michael Komarck","number":"109","power":null,"toughness":null,"loyalty":4,"multiverseid":238330,"imageName":"sorin markov","watermark":null,"setCode":"M12","imageCode":"m12","count":1},{"name":"Ugin's Construct","layout":"normal","manaCost":"{4}","cmc":4,"type":"Artifact Creature — Construct","rarity":"Uncommon","text":"When Ugin's Construct enters the battlefield, sacrifice a permanent that's one or more colors.","flavor":"While trapping the Eldrazi on Zendikar, Ugin learned little from Sorin, but he gleaned the rudiments of lithomancy from Nahiri.","artist":"Peter Mohrbacher","number":"164","power":"4","toughness":"5","loyalty":null,"multiverseid":391949,"imageName":"ugin's construct","watermark":null,"setCode":"FRF","imageCode":"frf","count":1}] \ No newline at end of file
+[{"name":"Callow Jushi","layout":"flip","manaCost":"{1}{U}{U}","cmc":3,"type":"Creature — Human Wizard","rarity":"Uncommon","text":"Whenever you cast a Spirit or Arcane spell, you may put a ki counter on Callow Jushi.\nAt the beginning of the end step, if there are two or more ki counters on Callow Jushi, you may flip it.","flavor":null,"artist":"Tsutomu Kawade","number":"31a","power":"2","toughness":"2","loyalty":null,"multiverseid":74489,"imageName":"callow jushi","watermark":null,"setCode":"BOK","imageCode":"bok"},{"name":"Coerced Confession","layout":"normal","manaCost":"{4}{U/B}","cmc":5,"type":"Sorcery","rarity":"Uncommon","text":"Target player puts the top four cards of his or her library into his or her graveyard. You draw a card for each creature card put into that graveyard this way.","flavor":"\"Ask the right questions in the right way and truth is inevitable.\"\n—Lazav","artist":"Mathias Kollros","number":"217","power":null,"toughness":null,"loyalty":null,"multiverseid":366408,"imageName":"coerced confession","watermark":"Dimir","setCode":"GTC","imageCode":"gtc"},{"name":"Khalni Hydra","layout":"normal","manaCost":"{G}{G}{G}{G}{G}{G}{G}{G}","cmc":8,"type":"Creature — Hydra","rarity":"Mythic Rare","text":"Khalni Hydra costs {G} less to cast for each green creature you control.\nTrample","flavor":"\"In ages past, bargains were struck and promises were made. Now we must collect on our debt. Begin the hymns.\"\n—Moruul, Khalni druid","artist":"Todd Lockwood","number":"192","power":"8","toughness":"8","loyalty":null,"multiverseid":193551,"imageName":"khalni hydra","watermark":null,"setCode":"ROE","imageCode":"roe"},{"name":"Nightmare","layout":"normal","manaCost":"{5}{B}","cmc":6,"type":"Creature — Nightmare Horse","rarity":"Rare","text":"Flying (This creature can't be blocked except by creatures with flying or reach.)\nNightmare's power and toughness are each equal to the number of Swamps you control.","flavor":"The thunder of its hooves beats dreams into despair.","artist":"Vance Kovacs","number":"276","power":"*","toughness":"*","loyalty":null,"multiverseid":383168,"imageName":"nightmare","watermark":null,"setCode":"M15","imageCode":"m15"},{"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","watermark":null,"setCode":"M15","imageCode":"m15"},{"name":"Disrupting Shoal","layout":"normal","manaCost":"{X}{U}{U}","cmc":2,"type":"Instant — Arcane","rarity":"Rare","text":"You may exile a blue card with converted mana cost X from your hand rather than pay Disrupting Shoal's mana cost.\nCounter target spell if its converted mana cost is X.","flavor":null,"artist":"Scott M. Fischer","number":"33","power":null,"toughness":null,"loyalty":null,"multiverseid":74128,"imageName":"disrupting shoal","watermark":null,"setCode":"BOK","imageCode":"bok"},{"name":"Sorin Markov","layout":"normal","manaCost":"{3}{B}{B}{B}","cmc":6,"type":"Planeswalker — Sorin","rarity":"Mythic Rare","text":"+2: Sorin Markov deals 2 damage to target creature or player and you gain 2 life.\n−3: Target opponent's life total becomes 10.\n−7: You control target player during that player's next turn.","flavor":null,"artist":"Michael Komarck","number":"109","power":null,"toughness":null,"loyalty":4,"multiverseid":238330,"imageName":"sorin markov","watermark":null,"setCode":"M12","imageCode":"m12"},{"name":"Ugin's Construct","layout":"normal","manaCost":"{4}","cmc":4,"type":"Artifact Creature — Construct","rarity":"Uncommon","text":"When Ugin's Construct enters the battlefield, sacrifice a permanent that's one or more colors.","flavor":"While trapping the Eldrazi on Zendikar, Ugin learned little from Sorin, but he gleaned the rudiments of lithomancy from Nahiri.","artist":"Peter Mohrbacher","number":"164","power":"4","toughness":"5","loyalty":null,"multiverseid":391949,"imageName":"ugin's construct","watermark":null,"setCode":"FRF","imageCode":"frf"}]
diff --git a/todo b/todo
index ef1b7ad..7644fcc 100644
--- a/todo
+++ b/todo
@@ -1,4 +1,5 @@
-TESTS
+parsing the new cardbase file format
+fix up the test files
-Cardbase:
+Cardbase tests:
* decks \ No newline at end of file