- New DeckWants SVar and class. Provides a way to give instructions for selecting related combo cards in random and Limited decks.

This commit is contained in:
mcrawford620
2012-07-15 05:37:10 +00:00
parent 19112a5a77
commit afa59c0596
25 changed files with 498 additions and 42 deletions

2
.gitattributes vendored
View File

@@ -11845,6 +11845,7 @@ src/main/java/forge/card/CardRules.java -text
src/main/java/forge/card/CardRulesReader.java svneol=native#text/plain
src/main/java/forge/card/CardSuperType.java -text
src/main/java/forge/card/CardType.java -text
src/main/java/forge/card/DeckWants.java -text
src/main/java/forge/card/EditionCollection.java svneol=native#text/plain
src/main/java/forge/card/EditionInfo.java svneol=native#text/plain
src/main/java/forge/card/FatPackData.java -text
@@ -12408,6 +12409,7 @@ src/test/java/forge/BoosterDraft1Test.java svneol=native#text/plain
src/test/java/forge/BoosterDraftTest.java svneol=native#text/plain
src/test/java/forge/CardColorTest.java svneol=native#text/plain
src/test/java/forge/CardReaderTest.java svneol=native#text/plain
src/test/java/forge/DeckWantsTest.java -text
src/test/java/forge/GuiDownloadPicturesLQTest.java svneol=native#text/plain
src/test/java/forge/GuiDownloadSetPicturesLQTest.java svneol=native#text/plain
src/test/java/forge/GuiMigrateLocalMWSSetPicturesHQTest.java svneol=native#text/plain

View File

@@ -5,6 +5,7 @@ Text:no text
T:Mode$ SpellCast | ValidCard$ Card.White | TriggerZones$ Battlefield | OptionalDecider$ You | Execute$ TrigGainLife | TriggerDescription$ Whenever a player casts a white spell, you may gain 1 life.
SVar:TrigGainLife:AB$GainLife | Cost$ 0 | LifeAmount$ 1
SVar:RemRandomDeck:True
SVar:DeckWants:Color$white
SVar:Rarity:Uncommon
SVar:Picture:http://www.wizards.com/global/images/magic/general/angels_feather.jpg
SetInfo:DST|Uncommon|http://magiccards.info/scans/en/ds/92.jpg

View File

@@ -7,6 +7,7 @@ SVar:TrigSac:AB$Sacrifice | Cost$ 0 | SacValid$ Creature.nonZombie | Defined$ Tr
T:Mode$ Phase | Phase$ End of Turn | TriggerZones$ Battlefield | IsPresent$ Creature | PresentCompare$ EQ0 | Execute$ TrigSacSelf | TriggerDescription$ At the beginning of the end step, if no creatures are on the battlefield, sacrifice CARDNAME.
SVar:TrigSacSelf:AB$Sacrifice | Cost$ 0 | Defined$ Self
SVar:RemRandomDeck:True
SVar:DeckWants:Type$Zombie
SVar:Rarity:Rare
SVar:Picture:http://www.wizards.com/global/images/magic/general/call_to_the_grave.jpg
SetInfo:SCG|Rare|http://magiccards.info/scans/en/sc/58.jpg

View File

@@ -5,6 +5,7 @@ Text:no text
A:AB$ Tap | Cost$ 3 T | ValidTgts$ Creature | TgtPrompt$ Select target creature | ConditionNotAllM12Empires$ True | SubAbility$ DBControl | SpellDescription$ Tap target creature. Gain control of that creature instead if you control artifacts named Scepter of Empires and Throne of Empires.
SVar:DBControl:DB$ GainControl | Defined$ Targeted | ConditionAllM12Empires$ True
SVar:RemRandomDeck:True
SVar:DeckWants:ListAll$Scepter of Empires|Throne of Empires
SVar:Rarity:Uncommon
SVar:Picture:http://www.wizards.com/global/images/magic/general/crown_of_empires.jpg
SetInfo:M12|Uncommon|http://magiccards.info/scans/en/m12/203.jpg

View File

@@ -5,6 +5,7 @@ Text:no text
T:Mode$ SpellCast | ValidCard$ Card.Black | TriggerZones$ Battlefield | OptionalDecider$ You | Execute$ TrigGainLife | TriggerDescription$ Whenever a player casts a black spell, you may gain 1 life.
SVar:TrigGainLife:AB$GainLife | Cost$ 0 | Defined$ You | LifeAmount$ 1
SVar:RemRandomDeck:True
SVar:DeckWants:Color$Black
SVar:Rarity:Uncommon
SVar:Picture:http://www.wizards.com/global/images/magic/general/demons_horn.jpg
SetInfo:DST|Uncommon|http://magiccards.info/scans/en/ds/116.jpg

View File

@@ -5,6 +5,7 @@ Text:no text
K:CARDNAME enters the battlefield tapped.
A:AB$ LosesGame | Cost$ W W U U B B R R G G T Sac<1/CARDNAME> | ValidTgts$ Player | TgtPrompt$ Select target player | SpellDescription$ Target player loses the game.
SVar:RemRandomDeck:True
SVar:DeckWants:ColorAll$white|blue|black|red|green
SVar:Rarity:Rare
SVar:Picture:http://www.wizards.com/global/images/magic/general/door_to_nothingness.jpg
SetInfo:M13|Rare|http://magiccards.info/scans/en/m13/203.jpg

View File

@@ -5,6 +5,7 @@ Text:no text
T:Mode$ SpellCast | ValidCard$ Card.Red | TriggerZones$ Battlefield | OptionalDecider$ You | Execute$ TrigGainLife | TriggerDescription$ Whenever a player casts a red spell, you may gain 1 life.
SVar:TrigGainLife:AB$GainLife | Cost$ 0 | Defined$ You | LifeAmount$ 1
SVar:RemRandomDeck:True
SVar:DeckWants:Color$Red
SVar:Rarity:Uncommon
SVar:Picture:http://www.wizards.com/global/images/magic/general/dragons_claw.jpg
SetInfo:DST|Uncommon|http://magiccards.info/scans/en/ds/117.jpg

View File

@@ -7,6 +7,7 @@ S:Mode$ Continuous | Affected$ Card.Self | AddPower$ 3 | AddToughness$ 3 | AddKe
SVar:X:Count$Valid Griffin.YouCtrl
SVar:BuffedBy:Griffin
SVar:RemRandomDeck:True
SVar:DeckWants:Type$Griffin
SVar:Rarity:Common
SVar:Picture:http://www.wizards.com/global/images/magic/general/griffin_rider.jpg
SetInfo:M12|Common|http://magiccards.info/scans/en/m12/20.jpg

View File

@@ -5,6 +5,7 @@ Text:no text
T:Mode$ SpellCast | ValidCard$ Card.Blue | TriggerZones$ Battlefield | OptionalDecider$ You | Execute$ TrigGainLife | TriggerDescription$ Whenever a player casts a blue spell, you may gain 1 life.
SVar:TrigGainLife:AB$GainLife | Cost$ 0 | Defined$ You | LifeAmount$ 1
SVar:RemRandomDeck:True
SVar:DeckWants:Color$Blue
SVar:Rarity:Uncommon
SVar:Picture:http://www.wizards.com/global/images/magic/general/krakens_eye.jpg
SetInfo:DST|Uncommon|http://magiccards.info/scans/en/ds/126.jpg

View File

@@ -6,6 +6,7 @@ PT:0/2
T:Mode$ SpellCast | ValidCard$ Enchantment | ValidActivatingPlayer$ You | Execute$ TrigDraw | TriggerZones$ Battlefield | OptionalDecider$ You | TriggerDescription$ Whenever you cast an enchantment spell, draw a card.
SVar:TrigDraw:AB$Draw | Cost$ 0 | Defined$ You | NumCards$ 1
SVar:RemRandomDeck:True
SVar:DeckWants:Type$Enchantment
SVar:Rarity:Rare
SVar:Picture:http://www.wizards.com/global/images/magic/general/mesa_enchantress.jpg
SetInfo:M10|Rare|http://magiccards.info/scans/en/m10/20.jpg

View File

@@ -6,6 +6,7 @@ PT:2/1
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ ScoutSearch | TriggerDescription$ When CARDNAME enters the battlefield, search your library for a creature card with deathtouch, hexproof, reach or trample and reveal it. Shuffle your library, then put that card on top of it.
SVar:ScoutSearch:DB$ ChangeZone | Origin$ Library | Destination$ Library | LibraryPosition$ 0 | ChangeNum$ 1 | ChangeType$ Creature.withDeathtouch+YouCtrl,Creature.withHexproof+YouCtrl,Creature.withReach+YouCtrl,Creature.withTrample+YouCtrl
SVar:RemRandomDeck:True
SVar:DeckWants:KeywordAny$Deathtouch|Hexproof|Reach|Trample
SVar:Rarity:Uncommon
SVar:Picture:http://www.wizards.com/global/images/magic/general/mwonvuli_beast_tracker.jpg
SetInfo:M13|Uncommon|http://magiccards.info/scans/en/m13/177.jpg

View File

@@ -7,6 +7,7 @@ K:Indestructible
T:Mode$ Always | TriggerZones$ Battlefield | IsPresent$ Permanent.counters_GE1_PHYLACTERY+YouCtrl | PresentCompare$ EQ0 | Execute$ TrigSac | TriggerDescription$ When you control no permanents with phylactery counters on them, sacrifice CARDNAME.
SVar:TrigSac:AB$Sacrifice | Cost$ 0 | Defined$ Self
SVar:RemRandomDeck:True
SVar:DeckWants:Type$Artifact
SVar:Rarity:Rare
SVar:Picture:http://www.wizards.com/global/images/magic/general/phylactery_lich.jpg
SetInfo:M11|Rare|http://magiccards.info/scans/en/m11/110.jpg

View File

@@ -7,6 +7,7 @@ T:Mode$ SpellCast | ValidCard$ Card.White,Card.Blue,Card.Black,Card.Red | ValidA
SVar:TrigPutCounter:AB$PutCounter | Cost$ 0 | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1
SVar:BuffedBy:Card.White,Card.Blue,Card.Black,Card.Red
SVar:RemRandomDeck:True
SVar:DeckWants:ColorAny$white|blue|black|red
SVar:Rarity:Rare
SVar:Picture:http://www.wizards.com/global/images/magic/general/quirion_dryad.jpg
SetInfo:M13|Rare|http://magiccards.info/scans/en/m13/184.jpg

View File

@@ -5,6 +5,7 @@ Text:no text
A:AB$ DealDamage | Cost$ T | Tgt$ TgtP | NumDmg$ X | References$ X | SpellDescription$ CARDNAME deals 1 damage to target player. It deals 3 damage to that player instead if you control artifacts named Crown of Empires and Throne of Empires.
SVar:X:Count$AllM12Empires.3.1
SVar:RemRandomDeck:True
SVar:DeckWants:ListAll$Throne of Empires|Crown of Empires
SVar:Rarity:Uncommon
SVar:Picture:http://www.wizards.com/global/images/magic/general/scepter_of_empires.jpg
SetInfo:M12|Uncommon|http://magiccards.info/scans/en/m12/216.jpg

View File

@@ -5,6 +5,7 @@ Text:no text
A:AB$ Token | Cost$ 1 T | TokenAmount$ X | References$ X | TokenName$ Soldier | TokenTypes$ Creature,Soldier | TokenOwner$ You | TokenColors$ White | TokenPower$ 1 | TokenToughness$ 1 | SpellDescription$ Put a 1/1 white Soldier creature token onto the battlefield. Put five of those tokens onto the battlefield instead if you control artifacts named Crown of Empires and Scepter of Empires.
SVar:X:Count$AllM12Empires.5.1
SVar:RemRandomDeck:True
SVar:DeckWants:ListAll$Scepter of Empires|Crown of Empires
SVar:Rarity:Rare
SVar:Picture:http://www.wizards.com/global/images/magic/general/throne_of_empires.jpg
SetInfo:M12|Rare|http://magiccards.info/scans/en/m12/221.jpg

View File

@@ -7,6 +7,7 @@ K:Flying
S:Mode$ Continuous | Affected$ Card.Self | AddHiddenKeyword$ HIDDEN CARDNAME can't attack. | CheckSVar$ X | SVarCompare$ EQ0 | Description$ CARDNAME can't attack unless you control a Knight or Soldier.
SVar:X:Count$Valid Kinght.YouCtrl,Soldier.YouCtrl
SVar:RemRandomDeck:True
SVar:DeckWants:TypeAny$Knight|Soldier
SVar:Rarity:Common
SVar:Picture:http://www.wizards.com/global/images/magic/general/war_falcon.jpg
SetInfo:M13|Common|http://magiccards.info/scans/en/m13/38.jpg

View File

@@ -5,6 +5,7 @@ Text:no text
T:Mode$ SpellCast | ValidCard$ Card.Green | TriggerZones$ Battlefield | OptionalDecider$ You | Execute$ TrigGainLife | TriggerDescription$ Whenever a player casts a green spell, you may gain 1 life.
SVar:TrigGainLife:AB$GainLife | Cost$ 0 | Defined$ You | LifeAmount$ 1
SVar:RemRandomDeck:True
SVar:DeckWants:Color$Green
SVar:Rarity:Uncommon
SVar:Picture:http://www.wizards.com/global/images/magic/general/wurms_tooth.jpg
SetInfo:DST|Uncommon|http://magiccards.info/scans/en/ds/162.jpg

View File

@@ -35,6 +35,7 @@ import com.esotericsoftware.minlog.Log;
import forge.card.CardCharacteristics;
import forge.card.CardManaCost;
import forge.card.DeckWants;
import forge.card.EditionInfo;
import forge.card.abilityfactory.AbilityFactory;
import forge.card.cardfactory.CardFactoryUtil;
@@ -224,6 +225,9 @@ public class Card extends GameEntity implements Comparable<Card> {
private Card haunting = null;
private Card effectSource = null;
private DeckWants deckWants = null;
private boolean isDeckWantsConstructed = false;
//private Map<String, String> sVars = new TreeMap<String, String>();
// hacky code below, used to limit the number of times an ability
@@ -8956,6 +8960,9 @@ public class Card extends GameEntity implements Comparable<Card> {
this.castFrom = castFrom0;
}
/**
* @return CardDamageHistory
*/
public CardDamageHistory getDamageHistory() {
return damageHistory;
}
@@ -8968,7 +8975,7 @@ public class Card extends GameEntity implements Comparable<Card> {
}
/**
* @param effectSource0 the effectSource to set
* @param src the effectSource to set
*/
public void setEffectSource(Card src) {
this.effectSource = src;
@@ -8982,10 +8989,29 @@ public class Card extends GameEntity implements Comparable<Card> {
}
/**
* @param startsGameInPlay0 the startsGameInPlay to set
* @param startsGameInPlay the startsGameInPlay to set
*/
public void setStartsGameInPlay(boolean startsGameInPlay) {
this.startsGameInPlay = startsGameInPlay;
}
/**
* @return the deckWants
*/
public DeckWants getDeckWants() {
if (!isDeckWantsConstructed) {
deckWants = new DeckWants(getSVar("DeckWants"));
isDeckWantsConstructed = true;
}
return deckWants;
}
/**
* @param deckWants the deckWants to set
*/
public void setDeckWants(DeckWants deckWants) {
this.deckWants = deckWants;
}
} // end Card class

View File

@@ -140,6 +140,23 @@ public class CardList implements Iterable<Card> {
return list;
} // getColor()
/**
* Get cards that match the given color string. Use the card's ManaCost
* to determine color.
* @param cardColor desired color
* @return CardList
*/
public final CardList getColorByManaCost(final String cardColor) {
final CardList ret = new CardList();
for (final Card c : this) {
ArrayList<String> colors = CardUtil.getOnlyColors(c);
if (colors.contains(cardColor)) {
ret.add(c);
}
}
return ret;
} // getColorByManaCost()
/**
* <p>
* getOnly2Colors.

View File

@@ -258,22 +258,22 @@ public final class CardUtil {
*/
public static ArrayList<String> getOnlyColors(final Card c) {
final CardManaCost m = c.getManaCost();
final byte color_profile = m.getColorProfile();
final byte colorProfile = m.getColorProfile();
final Set<String> colors = new HashSet<String>();
if ((color_profile & forge.card.CardColor.WHITE) > 0) {
if ((colorProfile & forge.card.CardColor.WHITE) > 0) {
colors.add(Constant.Color.WHITE);
}
if ((color_profile & forge.card.CardColor.BLACK) > 0) {
if ((colorProfile & forge.card.CardColor.BLACK) > 0) {
colors.add(Constant.Color.BLACK);
}
if ((color_profile & forge.card.CardColor.BLUE) > 0) {
if ((colorProfile & forge.card.CardColor.BLUE) > 0) {
colors.add(Constant.Color.BLUE);
}
if ((color_profile & forge.card.CardColor.RED) > 0) {
if ((colorProfile & forge.card.CardColor.RED) > 0) {
colors.add(Constant.Color.RED);
}
if ((color_profile & forge.card.CardColor.GREEN) > 0) {
if ((colorProfile & forge.card.CardColor.GREEN) > 0) {
colors.add(Constant.Color.GREEN);
}

View File

@@ -0,0 +1,159 @@
package forge.card;
import forge.CardList;
/**
* DeckWants provides the ability for a Card to "want" another Card or type of
* Cards in its random deck.
*
*/
public class DeckWants {
/**
* Enum of types of DeckWants.
*/
public enum Type { CARD, COLOR, COLORANY, COLORALL, KEYWORDANY, LISTALL, TYPE, TYPEANY, NONE }
private Type type = Type.NONE;
private String filterParam = null;
/**
* Construct a DeckWants from the SVar string.
*
* @param wants
* SVar for DeckWants
*/
public DeckWants(String wants) {
String[] pieces = wants.split("\\$");
if (pieces.length == 2) {
try {
Type typeValue = Type.valueOf(pieces[0].toUpperCase());
for (Type t : Type.values()) {
if (typeValue == t) {
type = t;
break;
}
}
} catch (IllegalArgumentException e) {
// type will remain NONE
}
filterParam = pieces[1];
}
}
/**
* @return the type
*/
public Type getType() {
return type;
}
/**
* Returns a list of Cards from the given CardList that match this
* DeckWants. I.e., other cards that this Card needs in its deck.
*
* @param cardList
* list of cards to be filtered
* @return CardList of Cards that match this DeckWants.
*/
public CardList filter(CardList cardList) {
CardList ret;
switch (type) {
case TYPE:
ret = cardList.getType(filterParam);
break;
case TYPEANY:
ret = new CardList();
String[] types = filterParam.split("\\|");
for (String type : types) {
CardList found = cardList.getType(type.trim());
if (found.size() > 0) {
ret.addAll(found);
}
}
case CARD:
ret = cardList.getName(filterParam);
break;
case COLOR:
ret = cardList.getColorByManaCost(filterParam.toLowerCase());
break;
case COLORANY:
ret = new CardList();
String[] colors = filterParam.split("\\|");
for (String color : colors) {
CardList found = cardList.getColorByManaCost(color.trim().toLowerCase());
if (found.size() > 0) {
ret.addAll(found);
}
}
break;
case COLORALL:
ret = new CardList();
int numFound = 0;
colors = filterParam.split("\\|");
for (String color : colors) {
CardList found = cardList.getColorByManaCost(color.trim().toLowerCase());
if (found.size() > 0) {
ret.addAll(found);
numFound++;
}
}
if (numFound < colors.length) {
ret.clear();
}
break;
case KEYWORDANY:
ret = new CardList();
String[] keywords = filterParam.split("\\|");
for (String keyword : keywords) {
CardList found = cardList.getKeyword(keyword.trim());
if (found.size() > 0) {
ret.addAll(found);
}
}
break;
case LISTALL:
ret = new CardList();
numFound = 0;
String[] names = filterParam.split("\\|");
for (String name : names) {
CardList found = cardList.getName(name.trim());
if (found.size() > 0) {
ret.addAll(found);
numFound++;
}
}
if (numFound < names.length) {
ret.clear();
}
break;
default:
ret = cardList;
break;
}
return ret;
}
/**
*
* Get the minimum number of "wanted" cards needed for the current card
* to be used in an AI deck.
*
* @return int
*/
public int getMinCardsNeeded() {
int ret;
switch (type) {
case LISTALL:
String[] names = filterParam.split("\\|");
ret = names.length;
break;
default:
ret = 1;
break;
}
return ret;
}
}

View File

@@ -11,6 +11,7 @@ import forge.CardListUtil;
import forge.Constant;
import forge.card.CardColor;
import forge.card.CardManaCost;
import forge.card.DeckWants;
import forge.card.mana.ManaCostShard;
import forge.deck.Deck;
import forge.util.MyRandom;
@@ -23,7 +24,7 @@ public class BoosterDeck extends Deck {
private static final long serialVersionUID = -7818685851099321964L;
private int cardsNeeded = 22;
private int numSpellsNeeded = 22;
private int landsNeeded = 18;
private DeckColors colors;
private CardList draftedList;
@@ -32,6 +33,14 @@ public class BoosterDeck extends Deck {
private CardList deckList = new CardList();
/**
* Constructor.
*
* @param dList
* list of cards drafted
* @param pClrs
* colors
*/
public BoosterDeck(CardList dList, DeckColors pClrs) {
super("");
this.draftedList = dList;
@@ -55,34 +64,40 @@ public class BoosterDeck extends Deck {
aiPlayables = draftedList.filter(new CardListFilter() {
@Override
public boolean addCard(final Card c) {
return !(c.getSVar("RemAIDeck").equals("True") || c.getSVar("RemRandomDeck").equals("True"));
boolean unPlayable = c.getSVar("RemAIDeck").equals("True");
unPlayable |= c.getSVar("RemRandomDeck").equals("True") && c.getSVar("DeckWants").equals("");
return !unPlayable;
}
});
draftedList.removeAll(aiPlayables);
// 1. Add any planeswalkers
CardList walkers = aiPlayables.getType("Planeswalker");
// 0. Add any planeswalkers
CardList walkers = aiPlayables.getOnly2Colors(colors.getColor1(), colors.getColor2()).getType("Planeswalker");
deckList.addAll(walkers);
cardsNeeded -= walkers.size();
aiPlayables.removeAll(walkers);
if (walkers.size() > 0) {
System.out.println("Added " + walkers.get(0).toString());
System.out.println("Planeswalker: " + walkers.get(0).toString());
}
// 0.5. Add combo cards (should this be done later? or perhaps within
// each method?)
addComboCards();
// 1. Add creatures, trying to follow mana curve
addManaCurveCreatures(15);
// 2.Try to fill up to 22 with on-color non-creature cards
addNonCreatures(cardsNeeded);
addNonCreatures(numSpellsNeeded - deckList.size());
// 3.Try to fill up to 22 with on-color creatures cards (if more than 15
// are present)
addBestCreatures(cardsNeeded);
addBestCreatures(numSpellsNeeded - deckList.size());
CardList nonLands = aiPlayables.getNotType("Land").getOnly2Colors(colors.getColor1(), colors.getColor2());
// 4. If there are still on-color cards and the average cmc is low add a
// 23rd card.
if (cardsNeeded == 0 && CardListUtil.getAverageCMC(deckList) < 3 && !nonLands.isEmpty()) {
if (deckList.size() == numSpellsNeeded && CardListUtil.getAverageCMC(deckList) < 3 && !nonLands.isEmpty()) {
Card c = nonLands.get(0);
deckList.add(c);
aiPlayables.remove(0);
@@ -91,14 +106,14 @@ public class BoosterDeck extends Deck {
// 5. If there are still less than 22 non-land cards add off-color
// cards.
addRandomCards(cardsNeeded);
addRandomCards(numSpellsNeeded - deckList.size());
// 6. If it's not a mono color deck, add non-basic lands that were
// drafted.
addNonBasicLands();
// 7. Fill up with basic lands.
final CCnt[] clrCnts = calculateLandNeeds();
if (landsNeeded > 0) {
addLands(clrCnts);
}
@@ -135,23 +150,26 @@ public class BoosterDeck extends Deck {
this.getMain().add(deckList);
this.getSideboard().add(aiPlayables);
this.getSideboard().add(draftedList);
/*
int i = 0;
System.out.println("DECK");
for (Card c : deckList) {
i++;
System.out.println(i + ". " + c.toString() + ": " + c.getManaCost().toString());
}
i = 0;
System.out.println("NOT PLAYABLE");
for (Card c : draftedList) {
i++;
System.out.println(i + ". " + c.toString() + ": " + c.getManaCost().toString());
}
i = 0;
System.out.println("NOT PICKED");
for (Card c : aiPlayables) {
i++;
System.out.println(i + ". " + c.toString() + ": " + c.getManaCost().toString());
}
*/
} else {
throw new RuntimeException("BoosterDraftAI : buildDeck() error, decksize not 40");
}
@@ -172,12 +190,13 @@ public class BoosterDeck extends Deck {
int landsAdded = 0;
for (int i = 0; i < 5; i++) {
if (clrCnts[i].getCount() > 0) {
if (clrCnts[i].getCount() > 0) {
// calculate number of lands for each color
final float p = (float) clrCnts[i].getCount() / (float) totalColor;
final int nLand = (int) (landsNeeded * p) + 1;
if (Constant.Runtime.DEV_MODE[0]) {
System.out.println("Basics[" + clrCnts[i].getColor() + "]: " + clrCnts[i].getCount() + "/" + totalColor + " = " + p + " = " + nLand);
System.out.println("Basics[" + clrCnts[i].getColor() + "]: " + clrCnts[i].getCount() + "/"
+ totalColor + " = " + p + " = " + nLand);
}
for (int j = 0; j <= nLand; j++) {
@@ -188,7 +207,7 @@ public class BoosterDeck extends Deck {
}
}
}
landsNeeded = landsNeeded - landsAdded;
int n = 0;
while (landsNeeded > 0) {
@@ -209,7 +228,7 @@ public class BoosterDeck extends Deck {
}
/**
* attempt to optimize basic land counts according to color representation
* attempt to optimize basic land counts according to color representation.
*
* @return CCnt
*/
@@ -218,7 +237,6 @@ public class BoosterDeck extends Deck {
new CCnt("Mountain", 0), new CCnt("Forest", 0) };
// count each card color using mana costs
// TODO: count hybrid mana differently?
for (int i = 0; i < deckList.size(); i++) {
final CardManaCost mc = deckList.get(i).getManaCost();
@@ -279,7 +297,6 @@ public class BoosterDeck extends Deck {
final Card c = z.get(MyRandom.getRandom().nextInt(z.size() - 1));
deckList.add(c);
cardsNeeded--;
nCards--;
aiPlayables.remove(c);
z.remove(c);
@@ -308,7 +325,6 @@ public class BoosterDeck extends Deck {
final Card c = others.get(index);
deckList.add(c);
cardsNeeded--;
nCards--;
aiPlayables.remove(c);
others.remove(c);
@@ -333,7 +349,6 @@ public class BoosterDeck extends Deck {
final Card c = creatures.get(0);
deckList.add(c);
cardsNeeded--;
nCreatures--;
aiPlayables.remove(c);
creatures.remove(c);
@@ -347,9 +362,9 @@ public class BoosterDeck extends Deck {
}
/**
* Add creatures to the deck, trying to follow some mana curve. Trying to have generous
* limits at each cost, but perhaps still too strict. But we're trying to prevent the AI
* from adding everything at a single cost.
* Add creatures to the deck, trying to follow some mana curve. Trying to
* have generous limits at each cost, but perhaps still too strict. But
* we're trying to prevent the AI from adding everything at a single cost.
*
* @param nCreatures
*/
@@ -397,7 +412,6 @@ public class BoosterDeck extends Deck {
if (willAddCreature) {
deckList.add(c);
cardsNeeded--;
nCreatures--;
aiPlayables.remove(c);
creatureCosts.put(cmc, creatureCosts.get(cmc) + 1);
@@ -419,4 +433,36 @@ public class BoosterDeck extends Deck {
}
}
/**
* Add cards that need other cards to be in the deck.
*/
public void addComboCards() {
CardList onColorPlayables = aiPlayables.getOnly2Colors(colors.getColor1(), colors.getColor2());
for (Card c : onColorPlayables) {
if (!c.getSVar("DeckWants").equals("")) {
DeckWants wants = c.getDeckWants();
CardList cards = wants.filter(onColorPlayables);
if (cards.size() >= wants.getMinCardsNeeded()) {
if (Constant.Runtime.DEV_MODE[0]) {
System.out.println("Adding " + c.getName() + " with up to " + cards.size() + " combo cards (e.g., "
+ cards.get(0).getName() + ").");
}
deckList.add(c);
aiPlayables.remove(c);
if (cards.size() <= 4) {
deckList.addAll(cards);
aiPlayables.removeAll(cards);
} else {
cards.shuffle();
for (int i = 0; i < 4; i++) {
Card theCard = cards.get(i);
deckList.add(theCard);
aiPlayables.remove(theCard);
}
}
}
}
}
}
}

View File

@@ -93,10 +93,9 @@ public class BoosterDraftAI {
final CardList aiPlayables = chooseFrom.filter(new CardListFilter() {
@Override
public boolean addCard(final Card c) {
if (c.getSVar("RemAIDeck").equals("True") || c.getSVar("RemRandomDeck").equals("True")) {
return false;
}
return true;
boolean unPlayable = c.getSVar("RemAIDeck").equals("True");
unPlayable |= c.getSVar("RemRandomDeck").equals("True") && c.getSVar("DeckWants").equals("");
return !unPlayable;
}
});
@@ -159,8 +158,9 @@ public class BoosterDraftAI {
hasPicked = true;
}
}
if (hasPicked && !pickedCard.isColorless()) {
this.playerColors.get(player).setColor2(pickedCard.getColor().get(0).toStringArray().get(0));
String pickedCardColor = pickedCard.getColor().get(0).toStringArray().get(0);
if (hasPicked && !pickedCard.isColorless() && !pickedCardColor.equals(this.playerColors.get(player).getColor1())) {
this.playerColors.get(player).setColor2(pickedCardColor);
if (Constant.Runtime.DEV_MODE[0]) {
System.out.println("Player[" + player + "] Color2: " + this.playerColors.get(player).getColor2());
}
@@ -352,7 +352,7 @@ public class BoosterDraftAI {
}
return out;
} // getDecks()
// returns 7 different ints, within the range of 0-9
/**
@@ -388,7 +388,7 @@ public class BoosterDraftAI {
this.deckColor[i] = this.deckColorChoices[n[i]];
}
// initilize color map
// initialize color map
BoosterDraftAI.colorToLand.put(Constant.Color.BLACK, "Swamp");
BoosterDraftAI.colorToLand.put(Constant.Color.BLUE, "Island");
BoosterDraftAI.colorToLand.put(Constant.Color.GREEN, "Forest");

View File

@@ -2038,7 +2038,8 @@ public class ComputerUtil {
/**
* Is it OK to cast this for less than the Max Targets?
* @param source
* @param source the source Card
* @return true if it's OK to cast this Card for less than the max targets
*/
public static boolean shouldCastLessThanMax(final Card source) {
boolean ret = true;

View File

@@ -0,0 +1,189 @@
package forge;
import java.io.File;
import java.util.List;
import org.testng.annotations.Test;
import junit.framework.Assert;
import forge.card.DeckWants;
import forge.properties.ForgeProps;
import forge.properties.NewConstants;
import forge.util.FileUtil;
/**
* Tests for DeckWants.
*
*/
@Test(timeOut = 1000, enabled = true)
public class DeckWantsTest {
/**
* Card test.
*/
@Test(timeOut = 1000, enabled = true)
void test() {
List<String> cardLines = FileUtil
.readFile(new File(ForgeProps.getFile(NewConstants.CARDSFOLDER) + "/g", "griffin_rider.txt"));
Card c = CardReader.readCard(cardLines);
Assert.assertEquals("Griffin Rider", c.getName());
Assert.assertNotNull(c.getDeckWants());
Assert.assertEquals(DeckWants.Type.TYPE, c.getDeckWants().getType());
cardLines = FileUtil
.readFile(new File(ForgeProps.getFile(NewConstants.CARDSFOLDER) + "/a", "assault_griffin.txt"));
Card assaultGriffin = CardReader.readCard(cardLines);
CardList cl = new CardList();
cl.add(assaultGriffin);
cardLines = FileUtil
.readFile(new File(ForgeProps.getFile(NewConstants.CARDSFOLDER) + "/a", "auramancer.txt"));
Card auramancer = CardReader.readCard(cardLines);
cl.add(auramancer);
Assert.assertEquals(1, c.getDeckWants().filter(cl).size());
Assert.assertEquals("Assault Griffin", c.getDeckWants().filter(cl).get(0).getName());
Assert.assertEquals(1, c.getDeckWants().getMinCardsNeeded());
}
/**
* Filter for cards.
*/
@Test(timeOut = 1000, enabled = true)
void testCards() {
List<String> cardLines = FileUtil
.readFile(new File(ForgeProps.getFile(NewConstants.CARDSFOLDER) + "/t", "throne_of_empires.txt"));
Card c = CardReader.readCard(cardLines);
Assert.assertEquals("Throne of Empires", c.getName());
Assert.assertNotNull(c.getDeckWants());
Assert.assertEquals(DeckWants.Type.LISTALL, c.getDeckWants().getType());
cardLines = FileUtil
.readFile(new File(ForgeProps.getFile(NewConstants.CARDSFOLDER) + "/a", "assault_griffin.txt"));
Card assaultGriffin = CardReader.readCard(cardLines);
CardList cl = new CardList();
cl.add(assaultGriffin);
cardLines = FileUtil
.readFile(new File(ForgeProps.getFile(NewConstants.CARDSFOLDER) + "/s", "scepter_of_empires.txt"));
Card sc = CardReader.readCard(cardLines);
cl.add(sc);
cardLines = FileUtil
.readFile(new File(ForgeProps.getFile(NewConstants.CARDSFOLDER) + "/c", "crown_of_empires.txt"));
Card cr = CardReader.readCard(cardLines);
cl.add(cr);
Assert.assertEquals(2, c.getDeckWants().filter(cl).size());
Assert.assertEquals(2, c.getDeckWants().getMinCardsNeeded());
}
/**
* Filter for keywords.
*/
@Test(timeOut = 1000, enabled = true)
void testKeywords() {
List<String> cardLines = FileUtil
.readFile(new File(ForgeProps.getFile(NewConstants.CARDSFOLDER) + "/m", "mwonvuli_beast_tracker.txt"));
Card c = CardReader.readCard(cardLines);
Assert.assertNotNull(c.getDeckWants());
Assert.assertEquals(DeckWants.Type.KEYWORDANY, c.getDeckWants().getType());
cardLines = FileUtil
.readFile(new File(ForgeProps.getFile(NewConstants.CARDSFOLDER) + "/a", "acidic_slime.txt"));
Card card = CardReader.readCard(cardLines);
CardList cl = new CardList();
cl.add(card);
cardLines = FileUtil
.readFile(new File(ForgeProps.getFile(NewConstants.CARDSFOLDER) + "/a", "ajanis_sunstriker.txt"));
Card card2 = CardReader.readCard(cardLines);
cl.add(card2);
Assert.assertEquals(1, c.getDeckWants().filter(cl).size());
Assert.assertEquals(1, c.getDeckWants().getMinCardsNeeded());
}
/**
* Filter for color.
*/
@Test(timeOut = 1000, enabled = true)
void testColor() {
List<String> cardLines = FileUtil
.readFile(new File(ForgeProps.getFile(NewConstants.CARDSFOLDER) + "/w", "wurms_tooth.txt"));
Card c = CardReader.readCard(cardLines);
Assert.assertNotNull(c.getDeckWants());
Assert.assertEquals(DeckWants.Type.COLOR, c.getDeckWants().getType());
cardLines = FileUtil
.readFile(new File(ForgeProps.getFile(NewConstants.CARDSFOLDER) + "/l", "llanowar_elves.txt"));
Card card = CardReader.readCard(cardLines);
CardList cl = new CardList();
cl.add(card);
cardLines = FileUtil
.readFile(new File(ForgeProps.getFile(NewConstants.CARDSFOLDER) + "/u", "unsummon.txt"));
card = CardReader.readCard(cardLines);
cl.add(card);
cl.getOnly2Colors("green", "white");
Assert.assertEquals(1, c.getDeckWants().filter(cl).size());
Assert.assertEquals(1, c.getDeckWants().getMinCardsNeeded());
}
/**
* Failing filter for cards.
*/
@Test(timeOut = 1000, enabled = true)
void testFailCards() {
List<String> cardLines = FileUtil
.readFile(new File(ForgeProps.getFile(NewConstants.CARDSFOLDER) + "/t", "throne_of_empires.txt"));
Card c = CardReader.readCard(cardLines);
Assert.assertEquals("Throne of Empires", c.getName());
Assert.assertNotNull(c.getDeckWants());
Assert.assertEquals(DeckWants.Type.LISTALL, c.getDeckWants().getType());
cardLines = FileUtil
.readFile(new File(ForgeProps.getFile(NewConstants.CARDSFOLDER) + "/a", "assault_griffin.txt"));
Card assaultGriffin = CardReader.readCard(cardLines);
CardList cl = new CardList();
cl.add(assaultGriffin);
cardLines = FileUtil
.readFile(new File(ForgeProps.getFile(NewConstants.CARDSFOLDER) + "/s", "scepter_of_empires.txt"));
Card sc = CardReader.readCard(cardLines);
cl.add(sc);
Assert.assertEquals(0, c.getDeckWants().filter(cl).size());
Assert.assertEquals(2, c.getDeckWants().getMinCardsNeeded());
}
/**
* Card test for junk deck wants.
*/
@Test(timeOut = 1000, enabled = true)
void testJunk() {
List<String> cardLines = FileUtil
.readFile(new File(ForgeProps.getFile(NewConstants.CARDSFOLDER) + "/g", "griffin_rider.txt"));
Card c = CardReader.readCard(cardLines);
c.setSVar("DeckWants", "Junk$Junk");
Assert.assertNotNull(c.getDeckWants());
Assert.assertEquals(DeckWants.Type.NONE, c.getDeckWants().getType());
}
/**
*
* Test for no wants.
*/
@Test(timeOut = 1000, enabled = true)
void testNoFilter() {
List<String> cardLines = FileUtil
.readFile(new File(ForgeProps.getFile(NewConstants.CARDSFOLDER) + "/a", "assault_griffin.txt"));
Card c = CardReader.readCard(cardLines);
Assert.assertEquals("Assault Griffin", c.getName());
Assert.assertNotNull(c.getDeckWants());
Assert.assertEquals(DeckWants.Type.NONE, c.getDeckWants().getType());
Card assaultGriffin = CardReader.readCard(cardLines);
CardList cl = new CardList();
cl.add(assaultGriffin);
Assert.assertEquals(1, c.getDeckWants().filter(cl).size());
}
}