It is currently 31 Oct 2025, 13:36
   
Text Size

CreatureValue

Post MTG Forge Related Programming Questions Here

Moderators: timmermac, Agetian, friarsol, Blacksmith, KrazyTheFox, CCGHQ Admins

CreatureValue

Postby Sloth » 05 Oct 2010, 15:29

Looking at the outdated code of AI_getBestCreature, I think we should design a function that evaluates a creature in order to improve the targeting and selecting of the AI. To get the best creature, the AI just takes the creature with the highest value.

I think it can be something like this:

converted mana cost
+ 2 x Power
+ Toughness

+ Power if Flying, Fear, Intimidate or unblockable
+ 2 x Power if Double Strike
+ Power if Lifelink
+ 2 x Points of Bushido
+ 2 x Each Instance of Flanking

etc.

Any suggestions?
User avatar
Sloth
Programmer
 
Posts: 3498
Joined: 23 Jun 2009, 19:40
Has thanked: 125 times
Been thanked: 507 times

Re: CreatureValue

Postby friarsol » 05 Oct 2010, 15:59

This looks like the early stages of threat modeling.

Unblockable should be its own thing since the AI can never block it, and will be placed on a clock.

Evasion should include Shadow along with the others you listed. Also, Evasion is less powerful if the AI has a way to block creatures with each Evasion Ability.
If the AI can either block without dying and/or kill the attacking creature based on creatures it has in play, the Evasion ability is not as powerful in the current game state. However, we can hold off on that type of logic until after we setup the base.

What we could do is put all these values into a text file and have them read on runtime to be more tweakable.

We may even be able to put them into the AI deck that is being played, and change the values based on the deck being played. If the AI has straight burn deck, he doesn't care about Evasion or Bushido or Flanking. He wasn't going to block anyway. But Lifelink, and Double Strike can cause major issues against the deck.
friarsol
Global Moderator
 
Posts: 7593
Joined: 15 May 2010, 04:20
Has thanked: 243 times
Been thanked: 965 times

Re: CreatureValue

Postby Rob Cashwalker » 05 Oct 2010, 16:20

I had been thinking of keeping it real simple.
cmc
+ power
+ toughness
+ 1 for each keyword (real keywords, like flying)
+ 1 for each ability (actual SpellAbilities)

Yeah, I get where you're going with giving evasion keywords a higher preference, but then we're just in for a maintenance nightmare every time a new keyword is added.
Some of the keywords we have like "when CARDNAME is put in the graveyard, do something else" should actually lower their score... in theory.

Let's say the effect is destroy target permanent. How would a 2 W 2/2 flier compare to a Glorious Anthem? The anthem doesn't have p/t or flying, just both have CMC of 3. By pure math, the flier will bite it, but in most cases the anthem would be the smarter target.

Don't get me wrong, I think we're on the right track with scoring in this manner, but it will have to be able to handle apples, oranges, bananas and grapes.
Maybe this isn't so critical. The current AI for DestroyTgt chooses the best target for each Valid type, then randomly picks a target from the more limited selection.
The Force will be with you, Always.
User avatar
Rob Cashwalker
Programmer
 
Posts: 2167
Joined: 09 Sep 2008, 15:09
Location: New York
Has thanked: 5 times
Been thanked: 40 times

Re: CreatureValue

Postby friarsol » 05 Oct 2010, 17:05

Right now, we would just be improving the apple code (creatures only). And then we can improve oranges, bananas and grapes separately. It's only when we need to compare them together that we would need a way to choose. Random of the best is fine for now, and doesn't change improving this code. If everything is given points based on the same scale, and we include card types and maybe subtypes, then we could just group it altogether and take the best one.

How about we do it like this, certain keywords will have specific values and anything leftover will just have a default value? That way we account for everything, but things that have negative value can have negative value, and things that should be more than the default have more than the default. And we don't have to scramble to give values to everything.

I would suggest starting with a base of 100 or 1000 and that way we can more easily give partial scoring while still being able to use an integer.

Each generic ability would be worth 10.

Some samples:

Serra Angel
100 + Power * 15 + Toughness * 10 + CMC * 5 + Power * 10(Flying) + Toughness * 5(Vigilance) + 10(1 Creature Type - Angel) = 295

Visara the Dreadful
100 + Power * 15 + Toughness * 10 + CMC * 5 + Power * 10(Flying) + 100(abDestroyTgtV, no Regen, any creature) + 10(1 Creature Type - Gorgon) + 20 (Legendary) = 435

Ball Lightning
100 + Power * 15 + Toughness + 10 + CMC * 5 + Power * 7 (Trample) + 20 (Haste) - 50 (At EOT, Sacrifice) + 10(1 Creature Type - Elemental) = 222
friarsol
Global Moderator
 
Posts: 7593
Joined: 15 May 2010, 04:20
Has thanked: 243 times
Been thanked: 965 times

Re: CreatureValue

Postby Rob Cashwalker » 05 Oct 2010, 18:07

It's like the SAT 100 points for writing your name....

I agree, whatever score is used, any given comparison must be creature vs creature, or enchantment vs enchantment. Then creatures can have a lower base value, (ie: 100) and planeswalkers could have a higher base value (ie: 500) and all other types in between. Then if the final choice becomes creature vs planeswalker, the planeswalker would still likely lose to Akroma 90% of the time.
The Force will be with you, Always.
User avatar
Rob Cashwalker
Programmer
 
Posts: 2167
Joined: 09 Sep 2008, 15:09
Location: New York
Has thanked: 5 times
Been thanked: 40 times

Re: CreatureValue

Postby Sloth » 05 Oct 2010, 19:30

friarsol wrote:Some samples:

Serra Angel
100 + Power * 15 + Toughness * 10 + CMC * 5 + Power * 10(Flying) + Toughness * 5(Vigilance) + 10(1 Creature Type - Angel) = 295

Visara the Dreadful
100 + Power * 15 + Toughness * 10 + CMC * 5 + Power * 10(Flying) + 100(abDestroyTgtV, no Regen, any creature) + 10(1 Creature Type - Gorgon) + 20 (Legendary) = 435

Ball Lightning
100 + Power * 15 + Toughness + 10 + CMC * 5 + Power * 7 (Trample) + 20 (Haste) - 50 (At EOT, Sacrifice) + 10(1 Creature Type - Elemental) = 222
I will implement this function and we can test and tweak it together (It's already much better than the simple comparisons in AI_getBestCreature). I will post the code later. Then we can discuss the keywords in detail.

Rob Cashwalker wrote:Yeah, I get where you're going with giving evasion keywords a higher preference, but then we're just in for a maintenance nightmare every time a new keyword is added.
Some of the keywords we have like "when CARDNAME is put in the graveyard, do something else" should actually lower their score... in theory.
I'm actually quite excited to evaluate the different keywords of creatures and I'm sure some non-developer forum members, who are more strategically involved in magic, have some good suggestions too.
User avatar
Sloth
Programmer
 
Posts: 3498
Joined: 23 Jun 2009, 19:40
Has thanked: 125 times
Been thanked: 507 times

Re: CreatureValue

Postby Sloth » 05 Oct 2010, 20:01

Here is a first draft:

Code: Select all
    public static int evaluateCreature(Card c) {

        int value = 100;
        int power = c.getNetAttack();
        int toughness = c.getNetDefense();
       
        //Doran
        if (AllZoneUtil.isCardInPlay("Doran, the Siege Tower")) power = toughness;
       
        value = value + power * 15;
        value = value + toughness * 10;
        value = value + c.getCMC() * 5;
       
        //Evasion keywords
        if (c.hasKeyword("Flying")) value = value + power * 10;
        if (c.hasKeyword("Horsemanship")) value = value + power * 10;
        if (c.hasKeyword("Unblockable")) value = value + power * 10;
        if (c.hasKeyword("Fear")) value = value + power * 5;
        if (c.hasKeyword("Intimidate")) value = value + power * 5;
       
        //Battle stats increasing keywords
        if (c.hasKeyword("Double Strike")) value = value + power * 15;
        value = value + getTotalBushidoMagnitude(c) * 20;
        value = value + getTotalFlankingMagnitude(c) * 20;
       
        //Other good keywords
        if (c.hasKeyword("Deathtouch")) value = value + 20;
       
        //Bad keywords
        if (c.hasKeyword("Defender")) value = value - power * 10 - 50;
        if (c.hasKeyword("CARDNAME can't block.")) value = value - 10;
       
        return value;
       
    }
User avatar
Sloth
Programmer
 
Posts: 3498
Joined: 23 Jun 2009, 19:40
Has thanked: 125 times
Been thanked: 507 times

Re: CreatureValue

Postby friarsol » 05 Oct 2010, 20:21

That's a good start. One thing to make this all cleaner
Use
Code: Select all
value += 2;
instead of
Code: Select all
value = value + 2;
friarsol
Global Moderator
 
Posts: 7593
Joined: 15 May 2010, 04:20
Has thanked: 243 times
Been thanked: 965 times

Re: CreatureValue

Postby Sloth » 05 Oct 2010, 20:48

OK, I will submit this:

Code: Select all
    public static int evaluateCreature(Card c) {

        int value = 100;
        int power = c.getNetAttack();
        int toughness = c.getNetDefense();
       
        //Doran
        if (AllZoneUtil.isCardInPlay("Doran, the Siege Tower")) power = toughness;
       
        value += power * 15;
        value += toughness * 10;
        value += c.getCMC() * 5;
       
        //Evasion keywords
        if (c.hasKeyword("Flying")) value += power * 10;
        if (c.hasKeyword("Horsemanship")) value += power * 10;
        if (c.hasKeyword("Unblockable")) value += power * 10;
        if (c.hasKeyword("Fear")) value += power * 5;
        if (c.hasKeyword("Intimidate")) value += power * 5;
        if (c.hasStartOfKeyword("CARDNAME can't be blocked except by")) value += power * 5;
       
        //Battle stats increasing keywords
        if (c.hasKeyword("Double Strike")) value += power * 15;
        value += getTotalBushidoMagnitude(c) * 20;
        value += getTotalFlankingMagnitude(c) * 20;
       
        //Other good keywords
        if (c.hasKeyword("Deathtouch")) value += 25;
        if (c.hasKeyword("Exalted")) value += 20;
        if (c.hasKeyword("First Strike")) value += 15;
        if (c.hasKeyword("Lifelink")) value += power * 10;
        if (c.hasKeyword("Rampage")) value += 2;
        if (c.hasKeyword("Reach")) value += 5;
        if (c.hasKeyword("Trample")) value += 15;
        if (c.hasKeyword("Vigilance")) value += power * 5 + toughness * 5;
        if (c.hasKeyword("Wither")) value += power * 10;
        if (c.hasKeyword("Annihilator")) value += 30;

        //Protection
        if (c.hasKeyword("Indestructible")) value += 70;
        if (c.hasKeyword("Shroud")) value += 25;
        if (c.hasKeyword("CARDNAME can't be the target of spells or abilities your opponents control.")) value += 35;
        if (c.hasStartOfKeyword("Protection from")) value += 20;
       
        //Bad keywords
        if (c.hasKeyword("Defender")) value -= power * 10 + 50;
        if (c.hasKeyword("CARDNAME can't block.")) value -= 10;
        if (c.hasKeyword("CARDNAME attacks each turn if able.")) value -= 5;
        if (c.hasKeyword("CARDNAME can block only creatures with flying.")) value -= toughness * 5;
        if (c.hasKeyword("CARDNAME can't attack or block.")) value -= power * 15 + toughness * 10;
        if (c.hasKeyword("CARDNAME can't block.")) value -= 10;
        if (c.hasKeyword("At the beginning of the end step, destroy CARDNAME.")) value -= 50;
        if (c.hasKeyword("At the beginning of the end step, exile CARDNAME.")) value -= 50;
        if (c.hasKeyword("At the beginning of the end step, sacrifice CARDNAME.")) value -= 50;
        if (c.hasStartOfKeyword("At the beginning of your upkeep, CARDNAME deals")) value -= 20;
        if (c.hasStartOfKeyword("At the beginning of your upkeep, destroy CARDNAME unless you pay")) value -= 20;
        if (c.hasStartOfKeyword("At the beginning of your upkeep, sacrifice CARDNAME unless you pay")) value -= 20;
        if (c.hasStartOfKeyword("Cumulative upkeep")) value -= 20;
        if (c.hasStartOfKeyword("Fading")) value -= 20;
        if (c.hasStartOfKeyword("Vanishing")) value -= 20;
       
        return value;
       
    } //evaluateCreature
User avatar
Sloth
Programmer
 
Posts: 3498
Joined: 23 Jun 2009, 19:40
Has thanked: 125 times
Been thanked: 507 times

Re: CreatureValue

Postby friarsol » 05 Oct 2010, 21:44

We should go through the Keywords forum post that Chris keeps and make sure any of those that are appropriate are added in.

"Protection from" needs to be expanded as they are different types of protection, and creatures may have more than one type of Protection.

Should Rampage and Annihilator get better based upon how much Rampaging or Annihilation is going on?

I would put parenthesis around any multiple clause for clarity (like Defender's)
Code: Select all
value -= (power * 10 + 50);
I think Fading and Vanishing get removed as a keyword currently. But I think I'll try to rewrite the code, so the keyword's don't get removed since this is the second time they would have been useful staying on. So leave those in there.
friarsol
Global Moderator
 
Posts: 7593
Joined: 15 May 2010, 04:20
Has thanked: 243 times
Been thanked: 965 times

Re: CreatureValue

Postby slapshot5 » 06 Oct 2010, 03:03

friarsol wrote:Should Rampage and Annihilator get better based upon how much Rampaging or Annihilation is going on?
Just a note: Rampage is next to useless unless a creature has trample. Then, it becomes one of the most useful abilities.

-slapshot5
slapshot5
Programmer
 
Posts: 1391
Joined: 03 Jan 2010, 17:47
Location: Mac OS X
Has thanked: 25 times
Been thanked: 68 times

Re: CreatureValue

Postby friarsol » 06 Oct 2010, 03:25

Actually, it's useless if there's no way to get your opponent to block with multiple creatures. Trample does help run over everything, but if you give a non-Trampling, Rampager Lure it can bust some heads pretty good too.
Either way if you had two 3/3 vanilla creatures and one has Rampage 2 and one has Rampage 3. The Rampage 3 one is marginally better.
friarsol
Global Moderator
 
Posts: 7593
Joined: 15 May 2010, 04:20
Has thanked: 243 times
Been thanked: 965 times

Re: CreatureValue

Postby Sloth » 06 Oct 2010, 12:33

I went through all the keywords and added a lot of them. One point of Rampage now adds (the lowest possible bonus) 1 to value. Feel free to expand this (more complex calculations, differentiate Protection, combos etc.)

Here it is:
Code: Select all
    public static int evaluateCreature(Card c) {

        int value = 100;
        int power = c.getNetAttack();
        int toughness = c.getNetDefense();
       
        //Doran
        if (AllZoneUtil.isCardInPlay("Doran, the Siege Tower")) power = toughness;
       
        value += power * 15;
        value += toughness * 10;
        value += c.getCMC() * 5;
       
        //Evasion keywords
        if (c.hasKeyword("Flying")) value += power * 10;
        if (c.hasKeyword("Horsemanship")) value += power * 10;
        if (c.hasKeyword("Unblockable")) value += power * 10;
        if (c.hasKeyword("Fear")) value += power * 5;
        if (c.hasKeyword("Intimidate")) value += power * 5;
        if (c.hasStartOfKeyword("CARDNAME can't be blocked except by")) value += power * 5;
        if (c.hasStartOfKeyword("CARDNAME can't be blocked by")) value += power * 2;
       
        //Battle stats increasing keywords
        if (c.hasKeyword("Double Strike")) value += power * 15;
        value += c.getKeywordMagnitute("Bushido") * 20;
        value += c.getAmountOfKeyword("Flanking") * 20;
       
        //Other good keywords
        if (c.hasKeyword("Deathtouch") && power > 0) value += 25;
        value += c.getAmountOfKeyword("Exalted") * 20;
        if (c.hasKeyword("First Strike") && !c.hasKeyword("Double Strike") && power > 0) value += 15;
        if (c.hasKeyword("Lifelink")) value += power * 10;
        if (c.hasKeyword("Trample")) value += power * 3;
        if (c.hasKeyword("Vigilance")) value += power * 5 + toughness * 5;
        if (c.hasKeyword("Wither")) value += power * 10;
        value += c.getKeywordMagnitute("Rampage");
        value += c.getKeywordMagnitute("Annihilator") * 30;
        if (c.hasKeyword("Changeling")) value += 5;
        if (c.hasKeyword("Whenever CARDNAME becomes blocked by a creature, destroy that creature at end of combat") && power > 0) value += 15;
        if (c.hasKeyword("Whenever a creature dealt damage by CARDNAME this turn is put into a graveyard, put a +1/+1 counter on CARDNAME.") && power > 0) value += 2;
       
        //Defensive Keywords
        if (c.hasKeyword("Reach")) value += 5;
        if (c.hasKeyword("CARDNAME can block creatures with shadow as though they didn't have shadow.")) value += 3;
        if (c.hasKeyword("Whenever CARDNAME blocks a creature, destroy that creature at end of combat")) value += 15;

        //Protection
        if (c.hasKeyword("Indestructible")) value += 70;
        if (c.hasKeyword("Shroud")) value += 30;
        if (c.hasKeyword("CARDNAME can't be the target of spells or abilities your opponents control.")) value += 35;
        if (c.hasStartOfKeyword("Protection from")) value += 20;
       
        //Activated Abilities
        if (c.hasStartOfKeyword("ab")) value += 10;
       
        //Bad keywords
        if (c.hasKeyword("Defender") || c.hasKeyword("CARDNAME can't attack.")) value -= power * 10 + 50;
        if (c.hasKeyword("CARDNAME can't block.")) value -= 10;
        if (c.hasKeyword("CARDNAME attacks each turn if able.")) value -= 10;
        if (c.hasKeyword("CARDNAME can block only creatures with flying.")) value -= toughness * 5;
       
        if (c.hasKeyword("CARDNAME can't attack or block.")) value = 100 + c.getCMC() * 5; //reset everything - useless
        if (c.hasKeyword("At the beginning of the end step, destroy CARDNAME.")) value -= 50;
        if (c.hasKeyword("At the beginning of the end step, exile CARDNAME.")) value -= 50;
        if (c.hasKeyword("At the beginning of the end step, sacrifice CARDNAME.")) value -= 50;
        if (c.hasStartOfKeyword("At the beginning of your upkeep, CARDNAME deals")) value -= 20;
        if (c.hasStartOfKeyword("At the beginning of your upkeep, destroy CARDNAME unless you pay")) value -= 20;
        if (c.hasStartOfKeyword("At the beginning of your upkeep, sacrifice CARDNAME unless you pay")) value -= 20;
        if (c.hasStartOfKeyword("Upkeep:")) value -= 20;
        if (c.hasStartOfKeyword("Cumulative upkeep")) value -= 30;
        if (c.hasStartOfKeyword("Fading")) value -= 20; //not used atm
        if (c.hasStartOfKeyword("Vanishing")) value -= 20; //not used atm
       
        //undesired effects
        if (c.hasStartOfKeyword("When CARDNAME is put into a graveyard from the battlefield, return CARDNAME to its owner's hand.")) value -= 10;
       
        return value;
       
    } //evaluateCreature
User avatar
Sloth
Programmer
 
Posts: 3498
Joined: 23 Jun 2009, 19:40
Has thanked: 125 times
Been thanked: 507 times


Return to Developer's Corner

Who is online

Users browsing this forum: No registered users and 34 guests

Main Menu

User Menu

Our Partners


Who is online

In total there are 34 users online :: 0 registered, 0 hidden and 34 guests (based on users active over the past 10 minutes)
Most users ever online was 9298 on 10 Oct 2025, 12:54

Users browsing this forum: No registered users and 34 guests

Login Form