*Author

Offline OldTreesTopic starter

  • Legendary Member
  • ******
  • Posts: 10297
  • Reputation Power: 114
  • OldTrees is a mythical and divine giver of immortality, one of the Turquoise Nymphs.OldTrees is a mythical and divine giver of immortality, one of the Turquoise Nymphs.OldTrees is a mythical and divine giver of immortality, one of the Turquoise Nymphs.OldTrees is a mythical and divine giver of immortality, one of the Turquoise Nymphs.OldTrees is a mythical and divine giver of immortality, one of the Turquoise Nymphs.OldTrees is a mythical and divine giver of immortality, one of the Turquoise Nymphs.OldTrees is a mythical and divine giver of immortality, one of the Turquoise Nymphs.OldTrees is a mythical and divine giver of immortality, one of the Turquoise Nymphs.OldTrees is a mythical and divine giver of immortality, one of the Turquoise Nymphs.OldTrees is a mythical and divine giver of immortality, one of the Turquoise Nymphs.OldTrees is a mythical and divine giver of immortality, one of the Turquoise Nymphs.OldTrees is a mythical and divine giver of immortality, one of the Turquoise Nymphs.OldTrees is a mythical and divine giver of immortality, one of the Turquoise Nymphs.OldTrees is a mythical and divine giver of immortality, one of the Turquoise Nymphs.OldTrees is a mythical and divine giver of immortality, one of the Turquoise Nymphs.
  • I was available for questions.
  • Awards: Brawl #2 Winner - Team FireTeam Card Design Winner
Card Balance Theory: The Relation between Card Cost and Card Value is nonlinear https://elementscommunity.org/forum/index.php?topic=57949.msg1184229#msg1184229
« on: March 31, 2015, 03:47:13 am »
Old Card Cost Theory has been useful but I have often mentioned that it was likely flawed due to its skyhook nature. Now I have found a ground from which a foundation can be built.

I created an optimization simulator.
Spoiler for Hidden:
Code is in Java
Code: [Select]
import java.util.Random;



public class EtgPseudocode
{
   public static void main(String[] args)
   {
      for(int lifespan=7; lifespan<=7; lifespan = lifespan+2)
      {
         
         for(int cost=0; cost<=4; cost = cost+2)
         {
            for(int turnOfNote=1; turnOfNote<=24; turnOfNote++)
            {
               optimize(turnOfNote, lifespan, cost);
            }
         }
     
   }   
}
   private static void optimize(int turnOfNote, int lifespan, int cost)
   {
      int numOfPills = 0;
      double bestAverage = 0;
      double averageTotalDamage = 0;
      double totalDamage;
      for(int ii=0; ii<=30; ii++)
      {
         totalDamage = 0;
         for(int jj=0; jj<500; jj++)
         {
            totalDamage += simulate(ii,turnOfNote,lifespan,cost);
         }
         averageTotalDamage = totalDamage/1000;//each of the 500 simulations returns 2 games
         if(averageTotalDamage > bestAverage)
         {
            numOfPills = ii;
            bestAverage = averageTotalDamage;
         }
      }
      System.out.println("Turn of Note:" + turnOfNote + ", Lifespan:" + lifespan
            + ", Casting Cost:" + cost + ", # of Pillars:" + numOfPills + ", Average Total Damage:" + bestAverage);
   }
   private static int simulate(int numOfPills, int turnOfNote, int lifespan, int cost)
   {
      int[] deck = new int[30];
      int[] creatures = new int[30];
      int deckIndex = 0;
      int creatureIndex;
      int pillars;
      int creaturesInHand;
      int quanta;
      int totalDamage = 0;
      int openingHandLength = 7;
      if(turnOfNote == 24)//if going second
      {
         openingHandLength = 6;
         //on the 24th turn going second would deckout so going second was ignored
      }
   
      for(int jj=0; jj<2; jj++)//once for going second(7+draw), once for going first(6+draw)
      {
      //Field strts empty
         for(int ii=0; ii<30; ii++)
         {
            creatures[ii]=0;
         }
      //Deck starts with 30 cards
         for(int ii=0; ii<30; ii++)//for each slot in the deck
         {
            if(ii<numOfPills)//put all the pillars in front and
            {
               deck[ii]=0; //set as a pillar
            }
            else
            {
               deck[ii]=1; //set as a creature
            }
         }
         
      //Deck is shuffled
         int tempValue = 0;
         int randIndex;
         Random rnd = new Random();
         for(int ii=30; ii>0; ii--)
         {
            tempValue = deck[ii-1];
            randIndex = rnd.nextInt(ii);
            deck[ii-1] = deck[randIndex];
            deck[randIndex] = tempValue;
         }
               
         pillars = 0;
         creaturesInHand = 0;
         creatureIndex = 0;
         
      //Draw opening hand
         for(int ii=0; ii<openingHandLength; ii++)//populate hand (draw 6/7 cards)
         {
            if(deck[ii] == 0)//if card is a pillar
            {
               pillars++;
            }
            else//if card is a creature
            {
               creaturesInHand++;
            }
         }
     
         deckIndex = openingHandLength;//keeps track of next card to draw
         quanta = 0;       
         
      //Run each turn     
         for(int ii=0; ii<turnOfNote; ii++)//for each turn
         {
         //draw a card
         if(deck[deckIndex] == 0)//if card is a pillar
            {
               pillars++;
            }
            else//if card is a creature
            {
               creaturesInHand++;
            }
            deckIndex++;
         
         //play creatures
            while(quanta >= cost)
            {
               if(creaturesInHand == 0)
               {
                  break;
               }
               quanta = quanta - cost;
               creatures[creatureIndex] = lifespan;
               creatureIndex++;
               creaturesInHand--;
            }
         
         //combat
            for(int kk=0; kk<creatures.length; kk++)
            {
               if(creatures[kk] > 0)
               {
                  totalDamage++;
                  creatures[kk] = creatures[kk] - 1;
               }
            }
         
         //end turn
            quanta = quanta + 1 + pillars;//generate quanta from mark and pillars
            if(creaturesInHand > 7)//discard if necessary
            {
               creaturesInHand = 7;
            }
         }
         openingHandLength = 6;
      }
      return totalDamage;
   }
}
Spoiler for copy of code:
import java.util.Random;



public class EtgPseudocode
{
   public static void main(String[] args)
   {
      for(int lifespan=7; lifespan<=7; lifespan = lifespan+2)
      {
         
         for(int cost=0; cost<=4; cost = cost+2)
         {
            for(int turnOfNote=1; turnOfNote<=24; turnOfNote++)
            {
               optimize(turnOfNote, lifespan, cost);
            }
         }
     
   }   
}
   private static void optimize(int turnOfNote, int lifespan, int cost)
   {
      int numOfPills = 0;
      double bestAverage = 0;
      double averageTotalDamage = 0;
      double totalDamage;
      for(int ii=0; ii<=30; ii++)
      {
         totalDamage = 0;
         for(int jj=0; jj<500; jj++)
         {
            totalDamage += simulate(ii,turnOfNote,lifespan,cost);
         }
         averageTotalDamage = totalDamage/1000;//each of the 500 simulations returns 2 games
         if(averageTotalDamage > bestAverage)
         {
            numOfPills = ii;
            bestAverage = averageTotalDamage;
         }
      }
      System.out.println("Turn of Note:" + turnOfNote + ", Lifespan:" + lifespan
            + ", Casting Cost:" + cost + ", # of Pillars:" + numOfPills + ", Average Total Damage:" + bestAverage);
   }
   private static int simulate(int numOfPills, int turnOfNote, int lifespan, int cost)
   {
      int[] deck = new int[30];
      int[] creatures = new int[30];
      int deckIndex = 0;
      int creatureIndex;
      int pillars;
      int creaturesInHand;
      int quanta;
      int totalDamage = 0;
      int openingHandLength = 7;
      if(turnOfNote == 24)//if going second
      {
         openingHandLength = 6;
         //on the 24th turn going second would deckout so going second was ignored
      }
   
      for(int jj=0; jj<2; jj++)//once for going second(7+draw), once for going first(6+draw)
      {
      //Field strts empty
         for(int ii=0; ii<30; ii++)
         {
            creatures[ii]=0;
         }
      //Deck starts with 30 cards
         for(int ii=0; ii<30; ii++)//for each slot in the deck
         {
            if(ii<numOfPills)//put all the pillars in front and
            {
               deck[ii]=0; //set as a pillar
            }
            else
            {
               deck[ii]=1; //set as a creature
            }
         }
         
      //Deck is shuffled
         int tempValue = 0;
         int randIndex;
         Random rnd = new Random();
         for(int ii=30; ii>0; ii--)
         {
            tempValue = deck[ii-1];
            randIndex = rnd.nextInt(ii);
            deck[ii-1] = deck[randIndex];
            deck[randIndex] = tempValue;
         }
               
         pillars = 0;
         creaturesInHand = 0;
         creatureIndex = 0;
         
      //Draw opening hand
         for(int ii=0; ii<openingHandLength; ii++)//populate hand (draw 6/7 cards)
         {
            if(deck[ii] == 0)//if card is a pillar
            {
               pillars++;
            }
            else//if card is a creature
            {
               creaturesInHand++;
            }
         }
     
         deckIndex = openingHandLength;//keeps track of next card to draw
         quanta = 0;       
         
      //Run each turn     
         for(int ii=0; ii<turnOfNote; ii++)//for each turn
         {
         //draw a card
         if(deck[deckIndex] == 0)//if card is a pillar
            {
               pillars++;
            }
            else//if card is a creature
            {
               creaturesInHand++;
            }
            deckIndex++;
         
         //play creatures
            while(quanta >= cost)
            {
               if(creaturesInHand == 0)
               {
                  break;
               }
               quanta = quanta - cost;
               creatures[creatureIndex] = lifespan;
               creatureIndex++;
               creaturesInHand--;
            }
         
         //combat
            for(int kk=0; kk<creatures.length; kk++)
            {
               if(creatures[kk] > 0)
               {
                  totalDamage++;
                  creatures[kk] = creatures[kk] - 1;
               }
            }
         
         //end turn
            quanta = quanta + 1 + pillars;//generate quanta from mark and pillars
            if(creaturesInHand > 7)//discard if necessary
            {
               creaturesInHand = 7;
            }
         }
         openingHandLength = 6;
      }
      return totalDamage;
   }
}
Sorry for my sparse commenting. Edit the Main method loops to set the values for the hypothetical creature you are testing.

That simulator would take 30 card decks comprised only of pillars and hypothetical creature A(attack 1, cost X, lives N turns) in an ideal ratio. The ideal ratio ignored card number limits and was calculated individually for every turn and every hypothetical creature examined. I collected the data here. I find the resulting graphs(sheet 2) to be most interesting.

Spoiler for graphs:





Now those graphs are a bit intimidating. However we can simplify that with an example. Let's take a metagame balanced around turn 10 where our creature in question would survive about 7 turns on average before falling to CC. If our creature cost 8, then we would expect to see 29 turns of action during a game in a deck optimized for its cost. If it cost 2 we would expect to see 59 turns of action during a game in a deck optimized for its cost. If the creature gives a linear contribution(say attacking) then a 2 cost version should be half as powerful as an 8 cost version since it would see twice as many turns. If it cost 4 it would see 44 turns and thus should be 2/3rds as powerful as the 8 cost version and 4/3rds as powerful as the 2 cost version. (See the non linear nature)

Now the first 3 charts have a flaw. They do not already account for the cost of the CC the opponent is theoretically using to shorten the lives of our creatures. That cost would need to be taken into account when making judgement calls about specific creature balancing.

Finally I will admit that this is more of a data dump than a conclusion dump. I am expecting many brains will be able to decipher the data better than just my own perspective. Plus data has this nice objective taste to it.




PS: Post 10,000! Yeah!
"It is common sense to listen to the wisdom of the wise. The wise are marked by their readiness to listen to the wisdom of the fool."
"Nothing exists that cannot be countered." -OldTrees on indirect counters
Ask the Idea Guru: http://elementscommunity.org/forum/index.php/topic,32272.0.htm

Offline ZephyrPhantom

  • Legendary Member
  • ******
  • Posts: 7675
  • Country: aq
  • Reputation Power: 101
  • ZephyrPhantom is a mythical and divine giver of immortality, one of the Turquoise Nymphs.ZephyrPhantom is a mythical and divine giver of immortality, one of the Turquoise Nymphs.ZephyrPhantom is a mythical and divine giver of immortality, one of the Turquoise Nymphs.ZephyrPhantom is a mythical and divine giver of immortality, one of the Turquoise Nymphs.ZephyrPhantom is a mythical and divine giver of immortality, one of the Turquoise Nymphs.ZephyrPhantom is a mythical and divine giver of immortality, one of the Turquoise Nymphs.ZephyrPhantom is a mythical and divine giver of immortality, one of the Turquoise Nymphs.ZephyrPhantom is a mythical and divine giver of immortality, one of the Turquoise Nymphs.ZephyrPhantom is a mythical and divine giver of immortality, one of the Turquoise Nymphs.ZephyrPhantom is a mythical and divine giver of immortality, one of the Turquoise Nymphs.ZephyrPhantom is a mythical and divine giver of immortality, one of the Turquoise Nymphs.ZephyrPhantom is a mythical and divine giver of immortality, one of the Turquoise Nymphs.ZephyrPhantom is a mythical and divine giver of immortality, one of the Turquoise Nymphs.ZephyrPhantom is a mythical and divine giver of immortality, one of the Turquoise Nymphs.ZephyrPhantom is a mythical and divine giver of immortality, one of the Turquoise Nymphs.
  • Awards: Slice of Elements 6th Birthday CakeSlice of Elements 4th Birthday CakeFlavour Text Revival Competition - WinnerSlice of Elements 3rd Birthday CakeSlice of Elements 2nd Birthday Cake
Re: Card Balance Theory: The Relation between Card Cost and Card Value is nonlinear https://elementscommunity.org/forum/index.php?topic=57949.msg1184236#msg1184236
« Reply #1 on: March 31, 2015, 04:23:45 am »
Fitting 10,000th; I see you're also quite the coder. Congratulations.

I don't have much additional information I can decipher from these graphs myself (yet), but I think a good starting point would be to take average CC into consideration - starting with a 4-5 damage CC costing 2-3 [element] (Shockwave,Rage Potion, Lightning Bolt). From there, one can scale a creature's HP appropriately and use the appropriate chart. (Reverse Time will need to be accounted for separately.)

Offline ARTHANASIOS

  • Legendary Member
  • ******
  • Posts: 3766
  • Country: gr
  • Reputation Power: 53
  • ARTHANASIOS brings all the vitality and activity of a Life Nymph.ARTHANASIOS brings all the vitality and activity of a Life Nymph.ARTHANASIOS brings all the vitality and activity of a Life Nymph.ARTHANASIOS brings all the vitality and activity of a Life Nymph.ARTHANASIOS brings all the vitality and activity of a Life Nymph.ARTHANASIOS brings all the vitality and activity of a Life Nymph.ARTHANASIOS brings all the vitality and activity of a Life Nymph.ARTHANASIOS brings all the vitality and activity of a Life Nymph.ARTHANASIOS brings all the vitality and activity of a Life Nymph.ARTHANASIOS brings all the vitality and activity of a Life Nymph.
  • Cockatrices ftw!
  • Awards: Slice of Elements 10th Birthday CakeCard Design Competition - Doomsday Device(TM)Weekly Tournament WinnerBrawl #2 Winner - Team FireSlice of Elements 4th Birthday CakeElemental Warrior Competition Winner
Re: Card Balance Theory: The Relation between Card Cost and Card Value is nonlinear https://elementscommunity.org/forum/index.php?topic=57949.msg1184254#msg1184254
« Reply #2 on: March 31, 2015, 08:23:44 am »
 With every respect to Trees, I think this card balance theory is going to fail because we, once again, try to compare apples to oranges. The stacks of quanta and the stack of a deck are two completely different kind of stacks. Moreover, cards are limited to a max of 8 and I can normally draw 1 per turn while quanta income per turn differs greatly (it can be anything between +1 for zero-cost decks and +312 for a 60-card deck with 6 SNs and 54 QTs).
 I find it unfortunate, but the only way to solve this is to stick to vanilla zero-cost cards like Photon or Spark for creatures, Soul Catcher for permanents and Holy Flash for spells and we build up our cost & effect according to those.
 However, I would really like to see if this is going to go somewhere and I will watch this topic for a while. Go for it, Trees! :D
Brawl #1 team :time, Brawl #2 team :fire, Brawl #3 team Silver Ferns, Brawl #4,7,8 Brawlmaster
War #8 team :life, War #10,11,12 team :light, Brawl #6 team FROGS :life

Offline Sera

  • Full Member
  • ***
  • Posts: 495
  • Reputation Power: 0
  • Sera hides under a Cloak.
  • Awards: Slice of Elements 10th Birthday CakeBattle League 1/2016 2nd PlaceSlice of Elements 7th Birthday CakeSlice of Elements 6th Birthday CakeBattle League 1/2015 1st Place
Re: Card Balance Theory: The Relation between Card Cost and Card Value is nonlinear https://elementscommunity.org/forum/index.php?topic=57949.msg1184256#msg1184256
« Reply #3 on: March 31, 2015, 08:42:02 am »
Honestly, this all looks a little like spaghetti to me right now, and I don't know much about coding either, so pardon me if I don't understand it too well.

rosutosefi made an attempt on showing that the cost to effect ratio is nonlinear, though I believe one major flaw of his simulation was that he was using the trend of existing ingame creatures as data. It doesn't make much sense to extrapolate data from data that were arbitrarily set beforehand, but it did make an interesting point about how cost is not proportional to QI. It is mostly affected by when you can "launch" the card on average, which isn't proportional to the cards you dedicate to produce quanta.

Maybe this is related to the fact that quanta production by pillars isn't linear, but closer to a square relation. Something like ∫xdx from 0 to the # of turns, where x is the number of pillars divided by the deck size, though this does not account for the initial 7 cards. Thus more expensive cards don't really get that much of a slower launch compared to less expensive cards, well, at least not linearly.

The fact that there are certain hp "sweet spots" should probably be considered as well (like how 1hp and 2hp are basically the same most of the time, etc), though it would be hard to draw objective data from it.
« Last Edit: March 31, 2015, 09:02:04 am by Puff »

Offline OldTreesTopic starter

  • Legendary Member
  • ******
  • Posts: 10297
  • Reputation Power: 114
  • OldTrees is a mythical and divine giver of immortality, one of the Turquoise Nymphs.OldTrees is a mythical and divine giver of immortality, one of the Turquoise Nymphs.OldTrees is a mythical and divine giver of immortality, one of the Turquoise Nymphs.OldTrees is a mythical and divine giver of immortality, one of the Turquoise Nymphs.OldTrees is a mythical and divine giver of immortality, one of the Turquoise Nymphs.OldTrees is a mythical and divine giver of immortality, one of the Turquoise Nymphs.OldTrees is a mythical and divine giver of immortality, one of the Turquoise Nymphs.OldTrees is a mythical and divine giver of immortality, one of the Turquoise Nymphs.OldTrees is a mythical and divine giver of immortality, one of the Turquoise Nymphs.OldTrees is a mythical and divine giver of immortality, one of the Turquoise Nymphs.OldTrees is a mythical and divine giver of immortality, one of the Turquoise Nymphs.OldTrees is a mythical and divine giver of immortality, one of the Turquoise Nymphs.OldTrees is a mythical and divine giver of immortality, one of the Turquoise Nymphs.OldTrees is a mythical and divine giver of immortality, one of the Turquoise Nymphs.OldTrees is a mythical and divine giver of immortality, one of the Turquoise Nymphs.
  • I was available for questions.
  • Awards: Brawl #2 Winner - Team FireTeam Card Design Winner
Re: Card Balance Theory: The Relation between Card Cost and Card Value is nonlinear https://elementscommunity.org/forum/index.php?topic=57949.msg1184270#msg1184270
« Reply #4 on: March 31, 2015, 12:55:25 pm »
With every respect to Trees, I think this card balance theory is going to fail because we, once again, try to compare apples to oranges. The stacks of quanta and the stack of a deck are two completely different kind of stacks. Moreover, cards are limited to a max of 8 and I can normally draw 1 per turn while quanta income per turn differs greatly (it can be anything between +1 for zero-cost decks and +312 for a 60-card deck with 6 SNs and 54 QTs).
 I find it unfortunate, but the only way to solve this is to stick to vanilla zero-cost cards like Photon or Spark for creatures, Soul Catcher for permanents and Holy Flash for spells and we build up our cost & effect according to those.
 However, I would really like to see if this is going to go somewhere and I will watch this topic for a while. Go for it, Trees! :D
I am not sure why the amount of quanta and the deck being made of cards would prevent drawing data. I did make sure that the deck used a queue data structure with a proper shuffle function. Also cards in the hand are limited to 8 cards in the simulator and the 8th card is discarded if not played that turn.

Your second point about Rainbow decks and 60 card decks is something to consider. However the simulator's optimizer already considers the cases for mono decks from 0-30 pillars so the data does cover a larger area than you credit it for.

I don't see why we would have to stick to vanilla 0 cost cards. I believe it is rather easy to compare Abomination(5 for 5) to Purple Dragon(10 for 10), we can see that Purple Dragon ranges from 25% weaker(for really short metagames, say with player health of 20) to 30% stronger(for really long metagames). (I used the indefinite table since I don't know how to value forcing the opponent to use CC).


Honestly, this all looks a little like spaghetti to me right now, and I don't know much about coding either, so pardon me if I don't understand it too well.

rosutosefi made an attempt on showing that the cost to effect ratio is nonlinear, though I believe one major flaw of his simulation was that he was using the trend of existing ingame creatures as data. It doesn't make much sense to extrapolate data from data that were arbitrarily set beforehand, but it did make an interesting point about how cost is not proportional to QI. It is mostly affected by when you can "launch" the card on average, which isn't proportional to the cards you dedicate to produce quanta.

Maybe this is related to the fact that quanta production by pillars isn't linear, but closer to a square relation. Something like ∫xdx from 0 to the # of turns, where x is the number of pillars divided by the deck size, though this does not account for the initial 7 cards. Thus more expensive cards don't really get that much of a slower launch compared to less expensive cards, well, at least not linearly.

The fact that there are certain hp "sweet spots" should probably be considered as well (like how 1hp and 2hp are basically the same most of the time, etc), though it would be hard to draw objective data from it.

I believe it is due to the twofold effect of pillars being cards. 1 pillars generate more quanta over time (close to a quadratic but can be simulated more accurately) and each pillar in the deck is a creature not in the deck. Trying to balance the creature:pillar ratio creates those roughly parallel curves in the graphs which means the relative heights do not maintain a constant ratio. Hence the relative value(and thus balanced strength) of an 8 cost over a 4 cost depends on how fast of a metagame those cards are setting.
"It is common sense to listen to the wisdom of the wise. The wise are marked by their readiness to listen to the wisdom of the fool."
"Nothing exists that cannot be countered." -OldTrees on indirect counters
Ask the Idea Guru: http://elementscommunity.org/forum/index.php/topic,32272.0.htm

 

anything
blarg: