Page 1 of 2

What card was discarded?

PostPosted: 27 Jul 2009, 02:45
by Rob Cashwalker
I'm working on a spDrawCards routine. There are very few pure draw card spells. The bulk of them come with conditions... like lose n life, or discard n cards... So I'm intending that nearly all combinations can be supported.

There's one syntax I've run into so far that's not as easy as I had thought - "discard 2 cards unless you discard an artifact".

This is what I've got down so far:
Code: Select all
    if (shouldSpDrawCards(card) != -1)
    {
       int n = shouldSpDrawCards(card);
       String parse = card.getKeyword().get(n).toString();
        card.removeIntrinsicKeyword(parse);
       
        String k[] = parse.split(":");
        final int NumCards[] = {-1};
        final String NumCardsType[] = {"none"};
        final boolean NumCardsTypeYouCtrl[] = {false};
        final boolean NumCardsTypeInPlay[] = {false};
        final boolean NumCardsTypeInYourYard[] = {false};
        final boolean NumCardsTypeInAllYards[] = {false};
        final boolean NumCardsDomain[] = {false};
        if (k[1].length() == 1)
           NumCards[0] = Integer.parseInt(k[1]);
        else
        {
           if (k[1].startsWith("NumCardsType"))
           {
              String kk[] = k[1].split("/");
              NumCardsType[0] = kk[1];
              NumCardsTypeYouCtrl[0] = kk[2].equals("YouCtrl");
              NumCardsTypeInPlay[0] = kk[2].equals("InPlay");
              NumCardsTypeInYourYard[0] = kk[2].equals("InYourYard");
              NumCardsTypeInAllYards[0] = kk[2].equals("InAllYards");
           }
           NumCardsDomain[0] = k[1].equals("Domain");
        }
       
        final int NumDiscard[] = {0};
        final boolean UnlessDiscardLand[] = {false};
        final boolean UnlessDiscardArtifact[] = {false};
        final boolean AtRandom[] = {false};
       
        final int NumLife[] = {0};
       
        final int NumToLibrary[] = {0};
        final boolean ToLibraryTop[] = {false};
        final boolean ToLibraryTopBottom[] = {false};
        final boolean ToLibraryBottom[] = {false};
       
        final int NumOppDraw[] = {0};
       
        if (k.length > 2)
        {
           if (k[2].contains("Discard"))
           {
              String kk[] = k[2].split("/");
              NumDiscard[0] = Integer.parseInt(kk[1]);
              if (kk.length > 2)
              {
                 UnlessDiscardLand[0] = kk[2].equals("UnlessDiscardLand");
                 UnlessDiscardArtifact[0] = kk[2].equals("UnlessDiscardArtifact");
                 AtRandom[0] = kk[2].equals("AtRandom");
              }
           }

           if (k[2].contains("Life"))
           {
              String kk[] = k[2].split("/");
              NumLife[0] = Integer.parseInt(kk[1]);
           }

           if (k[2].contains("NumToLibrary"))
           {
              String kk[] = k[2].split("/");
              NumToLibrary[0] = Integer.parseInt(kk[1]);
              if (kk.length > 2)
              {
                 ToLibraryTop[0] = kk[2].equals("Top");
                 ToLibraryTopBottom[0] = kk[2].equals("TopBottom");
                 ToLibraryBottom[0] = kk[2].equals("Bottom");
              }
           }
           
           if (k[2].contains("NumOppDraw"))
           {
              String kk[] = k[2].split("/");
              NumOppDraw[0] = Integer.parseInt(kk[1]);
           }
           
           final SpellAbility spell = new Spell(card)
           {
              public int getNumCards()
              {
                 int n = 0;
                String cardController = card.getController();
                PlayerZone myPlay = AllZone.getZone(Constant.Zone.Play, cardController);
                PlayerZone opPlay = AllZone.getZone(Constant.Zone.Play, AllZone.GameAction.getOpponent(cardController));
                
                PlayerZone myYard = AllZone.getZone(Constant.Zone.Graveyard, cardController);
                PlayerZone opYard = AllZone.getZone(Constant.Zone.Graveyard, AllZone.GameAction.getOpponent(cardController));
                
                CardList AllCards = new CardList();
                 
                 if (! NumCardsType[0].equals("none"))
                 {
                    
                    if (NumCardsTypeInYourYard[0] == false)
                       AllCards.addAll(myYard.getCards());

                    if (NumCardsTypeInAllYards[0] == false)
                    {
                       AllCards.addAll(myYard.getCards());
                       AllCards.addAll(opYard.getCards());
                    }
                    
                    if (NumCardsTypeYouCtrl[0] == true)
                       AllCards.addAll(myPlay.getCards());
                       
                    if (NumCardsTypeInPlay[0] == true)
                    {
                       AllCards.addAll(myPlay.getCards());
                       AllCards.addAll(opPlay.getCards());
                    }
                    
                    AllCards = AllCards.filter(new CardListFilter()
                    {
                       public boolean addCard(Card c)
                       {
                          if (c.getType().contains(NumCardsType[0]))
                             return true;
                          
                          return false;
                       }
                    });
                    
                    n = AllCards.size();
                 }
                 if (NumCardsDomain[0] == true)
                 {
                    AllCards.addAll(myPlay.getCards()); 
                    String basic[] = {"Forest", "Plains", "Mountain", "Island", "Swamp"};

                    for(int i = 0; i < basic.length; i++)
                    {
                       if (! AllCards.getType(basic[i]).isEmpty())
                          n++;
                    }
                 }
                 
                 return n;
              }
              public void resolve()
              {
                 if (NumCards[0] == -1)
                    NumCards[0] = getNumCards();
                 
                 for (int i=0; i<NumCards[0]; i++)
                    AllZone.GameAction.drawCard(card.getController());
                 
                 if (NumDiscard[0] > 0)
                 {
                    for (int i=0; i < NumDiscard[0]; i++)
                       if(card.getController().equals("Human"))
                          AllZone.InputControl.setInput(CardFactoryUtil.input_discard());
                       else
                          AllZone.GameAction.discardRandom("Computer");
                 }
              }
           };
        }
    }
The discard part is at the bottom, for reference:
Code: Select all
                 if (NumDiscard[0] > 0)
                 {
                    for (int i=0; i < NumDiscard[0]; i++)
                       if(card.getController().equals("Human"))
                          AllZone.InputControl.setInput(CardFactoryUtil.input_discard());
                       else
                          AllZone.GameAction.discardRandom("Computer");
                 }
We could break out of the for loop if there was an easy way to know which card was discarded. I don't think there's any existing card that does something like this....
So short of writing another form of input_discard, does anyone have some ideas?

Re: What card was discarded?

PostPosted: 27 Jul 2009, 11:49
by DennisBergkamp
Yeah I don't think so either... probably easiest (and useful) to just write some sort of input_discardXUnlessDiscardType(int x, String type).
They do get pretty tedious to write though after awhile :(

Re: What card was discarded?

PostPosted: 27 Jul 2009, 11:52
by Rob Cashwalker
DennisBergkamp wrote:Yeah I don't think so either... probably easiest (and useful) to just write some sort of input_discardXUnlessDiscardType(int x, String type).
They do get pretty tedious to write though after awhile :(
Yeah, especially since I'd have to also write an intelligent computer discard routine.

Re: What card was discarded?

PostPosted: 28 Jul 2009, 06:56
by Marek14
Also, it IS legal to discard an artifact AND another card (or even two artifact cards).

Re: What card was discarded?

PostPosted: 28 Jul 2009, 11:22
by Rob Cashwalker
[quote="Marek14"]Also, it IS legal to discard an artifact Ahh... true. That would make it tough because if an artifact is selected first, then the OK button needs to still be available, AND still permit you to select another card.

Re: What card was discarded?

PostPosted: 28 Jul 2009, 12:32
by zerker2000
Rob Cashwalker wrote:That would make it tough because if an artifact is selected first, then the OK button needs to still be available, AND still permit you to select another card.
How? Outline in semi-pseudo-code:
Code: Select all
Inputxtype(x, CardFilter type,Message){
dicarded=0;
canexit=false;
ShowMessage
{
  showMessage(Message);
  if(canexit) EnableOnlyOkButton();
  else DisableAllButtons();
}
onOk{stop();}
selectCard(card)
{
  if(card.getzone==hand)
  {
   discard(card);
   discarded++;
   if(type.addCard(card))
   {
     EnableOnlyOkButton();
     canexit=true;
   }
   if(discarded==x) onOK();
   else if(discarded>x){
     Exception tantrum = new Exception("Discarded " + discarded + " of " + x +" cards?")
     throw(tantrum);
   }
  }
}
Actually, the above could probably work as a first version after a spelling&grammar check(e.g. function names and type declarations).

Re: What card was discarded?

PostPosted: 29 Jul 2009, 06:31
by Marek14
Rob Cashwalker wrote:Ahh... true. That would make it tough because if an artifact is selected first, then the OK button needs to still be available, AND still permit you to select another card.
My solution would be:

If you have an artifact card in your hand, and at least two cards in total, you're asked if you want to discard an artifact, or to discard two cards. Based on your choice, you then perform the discard.

If you don't have an artifact, the mode "discard two" is selected automatically. If you have one card in your hand (artifact or not), it's discarded automatically, the same if you have exactly two cards and none is artifact. If you have empty hand, no choice is offered and nothing happens.

I think it might be easier for AI to grasp, like (I don't know the details): "Find the least valuable artifact, and sum of two least valuable cards, then discard the lowest value."

Re: What card was discarded?

PostPosted: 29 Jul 2009, 11:53
by Rob Cashwalker
I think I'm going to ignore this portion of the mechanic for a while. I'm not sure how the Input methods work. I've looked at the input_Discard method in CardFactoryUtil, and I haven't a clue how to modify it in order to deal with this. Zerker, when you get back, and school quiets down, you can add the functionality in.

I've now moved onto putting cards from your hand back to the library. This one seems like an easier hack from input_Discard.

Re: What card was discarded?

PostPosted: 30 Jul 2009, 14:54
by zerker2000
I Am currently on a computer on which I have put eclipse and am currently importing 7-29 source, but I shall be leaving tommorow at three: right now, I shall try to merge my old code into the latest release, post the results, and then probably get xtype discards working(when/if I get MPool/Abilities working). By the way, are there m/any cards that use a form of "discard <X> unless discard <type>" that isn't "2 or artifact"?

Re: What card was discarded?

PostPosted: 30 Jul 2009, 15:58
by Rob Cashwalker
At the moment, as best I can tell, there are two spells. One is 2 or artifact and the other is 2 or land. But you never know when there will be another or when we can reuse the overall logic.

Re: What card was discarded?

PostPosted: 31 Jul 2009, 17:08
by mtgrares
I'm probably the best one to answer this question. Below is the code for "discard 2 cards or an artifact card". Technically you can discard 2 artifact cards but that is a very rare case so I'm going to ignore it (for better or worse).

Note that I haven't compiled this code and there might be a few small errors. I just wrote this on the fly 2 minutes ago.

Code: Select all
//discard 2 cards or an artifact card
//note: you cannot discard 2 artifact cards which the rules allow
public static Input input_discard2orArtifact(final SpellAbility spell)
{
   //this must be final since it is used by an anonymous class (a class that isn't named)
   final int nDiscard = 2;

   Input target = new Input()
   {
      private static final long serialVersionUID = 2355676549786944050L;

      int n = 0;
      public void showMessage()
      {
         AllZone.Display.showMessage("Discard 2 cards unless you discard an artifact");
         //user cannot click on OK or Cancel buttons
         ButtonUtil.disableAll();

         //is your hand empty?
         if(AllZone.Human_Hand.getCards().length == 0)
            stop();
      }
      public void selectCard(Card card, PlayerZone zone)
      {
         if(zone.is(Constant.Zone.Hand))
         {
            AllZone.GameAction.discard(card);
            n++;
            
            if(card.isArtifact() || n == nDiscard)
              stop();
            
            //show message again to the user in case it might have changed
            //(but it won't in this case)
            //this also checks to see if the player's hand is empty
            showMessage();
         }//if

      }//selectCard()
   };//Input
   return target;
}//input_discard2orArtifact()

Re: What card was discarded?

PostPosted: 31 Jul 2009, 18:13
by Rob Cashwalker
Thanks Rares. I'm still puzzled by how the Input methods operate... but I guess that's one area where your GUI design rocks - there's very little code necessary to make custom actions like this.

Re: What card was discarded?

PostPosted: 31 Jul 2009, 18:24
by mtgrares
I know that Input is confusing. This is one of the earliest problems that I struggled to solve. How can a mouse click represent choosing a target with an infinite number of restrictions OR going to the next phase OR any number of other things.

I'm very happy that most of my code is readable and grokked (understood).

Re: What card was discarded?

PostPosted: 31 Jul 2009, 18:46
by mtgrares
And you could write an "intelligent" method for the computer to use like GameAction.discard2OrArtifact() or GameAction.AI_discard(String parseString).

Re: What card was discarded?

PostPosted: 01 Aug 2009, 03:08
by Rob Cashwalker
Yes, I've been considering an intelligent discard routine... besides just the 2 or artifact kind. Like discard land if the AI has at least 4 or 5 lands in play, discard the smallest vanilla creature, or the most expensive spell if early in the game....