Saturday, February 11, 2012

Browsing Forge’s Source Code – Part Deux

Similar to the line that Morpheus said in the Matrix, “Some things changes and some things don’t.” Much of Forge has been changed and improved but the core foundation is the same. Card objects, hold one or more SpellAbility objects. Today I will be looking through the “creature cardfactory” that handles the weird, oddball abilities that are not scripted. (Scripting only cares about multiple cards with the same ability.)

The creature cardfactory is relatively small, only 2,600 lines including blank lines, and handles around 69 creatures. (You can view the Java source code here.) The first creature is one that I recognize “Force of Savagery”. It is an enigmatic 8/0 creature that only costs 2G. Obviously the key is that it needs to be pumped up by Glorious Anthem or similar cards.

The code below is just for the AI and checks to see if Glorious Anthem or its green cousin Gaea's Anthem is in play. The problem is that checking for specific cards is that new cards are added/created all of the time.
if (cardName.equals("Force of Savagery")) 
{
  final SpellAbility spell = new SpellPermanent(card) 
  {
    public boolean canPlayAI() 
    {
      final CardList list = AllZone.getComputerPlayer().
          getCardsIn(Zone.Battlefield);

      return list.containsName("Glorious Anthem") 
          || list.containsName("Gaea's Anthem");
    }
  };

  card.clearFirstSpell();
  card.addSpellAbility(spell);
}

Drekavac is the next card that I recognize. I originally added it but the code has been updated as Forge has changed. Drekavac (2B, 3/3) the drawback is that you must discard a non-creature card. In the code that tells the AI not to play this card I see this comment.

I could never get the AI to work correctly, it always played the same card 2 or 3 times

This takes me back because that is my original comment. Now I know how to fix the problem but the AI probably should not play cards with a drawback anyways, since it cannot tell when the drawback is tactically prudent or not. I always liked using Drekavac for some reason.

The next card is Phylactery Lich. It is a pretty cool rare that gets you a 5/5 indestructible creature for only BBB if you own an artifact. The code here only deals with letting the player choose an artifact. Elsewhere, where static or state effects are checked, the code checks to see if the artifact with the phylactery counter is destroyed. The AI for this card is simple and just makes a “sanity check” to see if the computer has an artifact on the battlefield.
public boolean canPlayAI() 
{
return (!AllZone.getComputerPlayer()
  .getCardsIn(Zone.Battlefield)
  .getType("Artifact")
  .isEmpty() 
  
  && 
  
  AllZone.getZoneOf(this.getSourceCard()).
  is(Constant.Zone.Hand));
}
Sky Swallower is another card that I coded back in the day. Sky Swallower is another situational card that the AI plays badly. I thought it might be a good card but I don’t think I have ever won a game with it. I know it could win a game. In the code there is my original comment that has not been updated.

// TODO - this needs to be targeted

Technically Sky Swallower’s ability is targeted and Forge does not target the opponent because it makes the code a few lines shorter. I know that Magic has a few cards that give protection to a player but years ago, I knew that Forge did not have any of those cards. I used Forge’s excellent deck editor to search for cards that give protection to players and I could not find any but I could be wrong.

While these were just small snippets of code, it felt good to look at actual source code.

Huzzah,
mtgrares

p.s.
“Part deux” is taken from one of my favorite movies, “Hots Shots: Part Deux”. Deux is French for “two”.

6 comments:

Anonymous said...

Surely checking specifically for Glorious Anthem or Gaea's Anthem is the wrong way to go about it?

Other cards exist which could give it enough toughness to survive (eg Adaptive Automaton, Angelic Shield, Beastmaster Acension, Blessed Orator), or which could negate the bonus of having the Anthem in play (Ascendant Evincar, Crovax, Curse of Death's Hold, etc.).

The right way for the AI to handle this is to check what the toughness of a hypothetical 8/0 creature on the battlefield would be, and not cast it if it's less than 1!

Anonymous said...

Oh, and of course it shouldn't be specific to Force of Savagery either! It shouldn't cast any creature card without checking if it will be dead on arrival with the current battlefield conditions, unless it has a secondary 'on entering battlefield' or 'on cast' effect that makes it worthwhile.

Forge said...

"Surely checking specifically for Glorious Anthem or Gaea's Anthem is the wrong way to go about it?"

Yes and no. Yes it seems to work but no it isn't a perfect solution. Computer programming is where perfection meets reality.

Forge said...

Much of Forge is far from perfect but that doesn't mean that it isn't a great program. I talk about some of Forge's imperfections but that doesn't mean that I think Forge is badly written. The internals of any program are not perfect and this is one view of Forge's internals.

crapsticko said...

Bro can you fix the link to download Mage? It's blocked :( SOPA is messing with it I guess...

Anonymous said...

Mage is now here.
http://mtgathering.ru/mage/