Stupid or unlucky?

Discuss anything related to warbarons.

Re: Stupid or unlucky?

Postby piranha » Fri Sep 03, 2010 5:37 am

I decided to give the run the battle multiple times a try now.
I simply commented away all database entries and put a for loop around doing the whole battle 1000 times. It took 48 seconds to complete.
This is kind of what I expected. It would probably be possible to make it go faster by optimizing the code as much as possible but I doubt that it will be possible to make it go fast enough to not appear as if the game is broken, and with many games going it will take too much resources from the server.

We really need to find another solution to the problem, it might not be exactly the same but it might be good enough to make the game work and be fun. I'll post my javascript code from yesterday and tell you about my tests. Perhaps you will get some ideas.

I tested quite a lot of things and the best results I got was when each unit get 3 decks that it draw card from (the card deck never ran out with 3 decks per unit)

When a unit killed another unit its deck would receive new cards, cards with the value 20 o basically cards that are making it more difficult to hit again. The killed units strength dived by 2 was the amount of cards given per kill so for example if a STR 4 is killed 2 new 20s in placed in the deck of the attacker.

This makes it hard for one unit to kill a lot of other units, like the spider situation. The spider still won 1 of 10.000 making it 99.99% chance to win for Zajoman.

When I used the same system on the two hero armies I got 77% chance to win compared to 73% chance normally.

I'm posting the code, its quite messy but I added some comments in case you feel like trying. Just copy all and put in in a html file and run.
Code: Select all
<script type="text/javascript">

function WarlordsTest()
{
    //Data to show survivors
    var defoutline = new Array(0,0,0,0,0,0,0,0,0);
    var attoutline = new Array(0,0,0,0,0,0,0,0,0);
   
    var attwincount = 0;
    var defwincount = 0;
   var attdecklength = 0;
   var defdecklength = 0;
    var attwinning = 0;
    var defwinning = 0;
   const DIE = 20;
   var HITS_STACK1 = 2;
   var HITS_STACK2 = 2;
   

   const CYCLES = 10000;

    var strarrStack1 = new Array;
    var hitarrStack1 = new Array;

    var strarrStack2 = new Array;
    var hitarrStack2 = new Array;
   
    var deck1 = new Array;
    var deck2 = new Array;
   
   var hitsStack1;
   var hitsStack2;
   
   //Strength of the attackers
    strarrStack1[0] = 4;
    strarrStack1[1] = 4;
    strarrStack1[2] = 4;
    strarrStack1[3] = 4;
    strarrStack1[4] = 4;
    strarrStack1[5] = 7;
    strarrStack1[6] = 7;
    strarrStack1[7] = 8;

  //Strength of the defenders
   strarrStack2[0] = 9;
   /*
   strarrStack2[1] = 4;
   strarrStack2[2] = 5;
   strarrStack2[3] = 5;
   strarrStack2[4] = 8;
   strarrStack2[5] = 8;
   strarrStack2[6] = 6;
   strarrStack2[7] = 12;
       
*/

//Hitpoints of the attackers
   hitarrStack1[0] = 2;
   hitarrStack1[1] = 2;
   hitarrStack1[2] = 2;
   hitarrStack1[3] = 2;
   hitarrStack1[4] = 2;
   hitarrStack1[5] = 2;
   hitarrStack1[6] = 2;
   hitarrStack1[7] = 2;
   
//Hitpoints of the defenders
   hitarrStack2[0] = 2;
/*
   hitarrStack2[1] = 2;

   hitarrStack2[2] = 2;
   hitarrStack2[3] = 2;
   hitarrStack2[4] = 2;
   hitarrStack2[5] = 2;
   hitarrStack2[6] = 2;
   hitarrStack2[7] = 2;
       
   */
   


   //var defdeck = deck1[3];
   //alert(defdeck);
   var attnodeck = 1;
   var defnodeck = 1;
   var r = 0;
   var q = 0;
   var winsStack1 = 0;
   var winsStack2 = 0;
   var unitsLeftStack1 = 0;
   var unitsLeftStack2 = 0;

   for (i = 0; i < CYCLES; i++)
   {

  //Creating the deck arrys
   for(var y=0; y<hitarrStack1.length; y++)
   {
   
   
   deck1[y] = new Array(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20);

   deck1[y] = shuffle(deck1[y]);
   
   }

   for(var a=0; a<hitarrStack2.length; a++)
   {
   
   
   deck2[a] = new Array(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20);

   deck2[a] = shuffle(deck2[a]);
   
   }
 
        var attdeck = deck1[0];
        var defdeck = deck2[0];
      //hitsStack1 = HITS_STACK1;
      //hitsStack2 = HITS_STACK2;
     
      defnodeck = 1;
      attnodeck = 1;
      r = 0;
      q = 0;
     
     
    for(var p=0; p < 250; p++)
    {
        //window.document.write(r+","+hitarrStack2.length+"<br />");

         
      while (hitarrStack1[q] > 0 && hitarrStack2[r] > 0)
      {
       
         //var strStack2  = strarrStack2[r];
         //var hitsStack2 = hitarrStack2[r];
         


         //If the deck only have 1 card left pick it and create a new deck, this is used if you decrese the amount of cards per deck
         if(attdeck.length == 1)
         {
   
             var rollStack1 = attdeck.shift();
             attnodeck = attnodeck+0;
             var k=0;
             for(var h=20; h>attnodeck; h--)
             {
                attdeck[k] = h;
                k++;
             }
             attdeck = shuffle(attdeck);
         }

         
         if(defdeck.length == 1)
         {
           
             var rollStack2 = defdeck.shift();
             defnodeck = defnodeck+0;
             var k=0;
             for(var h=20; h>defnodeck; h--)
             {
                defdeck[k] = h;
                k++;
             }

             defdeck = shuffle(defdeck);
         }
         
         //Draw cards from deck
        var rollStack1 = attdeck.shift();
        var rollStack2 = defdeck.shift();
         
         if (rollStack1 <= strarrStack1[q] && rollStack2 > strarrStack2[r]) hitarrStack2[r]--;
         if (rollStack2 <= strarrStack2[r] && rollStack1 > strarrStack1[q]) hitarrStack1[q]--;
         
       
         
         
        //var rollStack1 = GetRandomInt(1, DIE);
         //var rollStack2 = GetRandomInt(1, DIE);


      }
        //alert (hitsStack1+","+hitarrStack2[r]);
      if (hitarrStack1[q] == 0)
      {
        //If attacker is killed add cards to enemy deck
         q++;
        var attdeck = deck1[q];
       
        var str = strarrStack1[q];
        var str = Math.ceil(str / 2);
        for(f=0; f<str; f++)
        {
            defdeck.push(20);
        }
       
       
        defdeck = shuffle(defdeck);
       
         if(q == hitarrStack1.length) {break;}
         
         
         //unitsLeftStack2 = unitsLeftStack2 + (hitsStack2 + 1) / 2;
      }
      if (hitarrStack2[r] == 0)
      {
        //If defender is killed add cards to enemy deck
         r++;
         var defdeck = deck2[r];
         
         
         var str = strarrStack2[r];
         var str = Math.ceil(str / 2);
        for(f=0; f<str; f++)
        {
            attdeck.push(20);
        }
         
         attdeck = shuffle(attdeck);
         
         if(r == hitarrStack2.length) {break;}
         //winsStack1++;
         //unitsLeftStack1 = unitsLeftStack1 + (hitsStack1 + 1) / 2;
      }
     
      }
     
      //If army is dead give new hitpoints to all units  and save a bunch of data
      if(r == hitarrStack2.length)
      {
        winsStack1++;
   hitarrStack2[0] = 2;
         /*
   hitarrStack2[1] = 2;

   hitarrStack2[2] = 2;
   hitarrStack2[3] = 2;
   hitarrStack2[4] = 2;
   hitarrStack2[5] = 2;
   hitarrStack2[6] = 2;
   hitarrStack2[7] = 2;
     
       */
   hitarrStack1[0] = 2;

   hitarrStack1[1] = 2;
   hitarrStack1[2] = 2;
   hitarrStack1[3] = 2;
   hitarrStack1[4] = 2;
   hitarrStack1[5] = 2;
   hitarrStack1[6] = 2;
   hitarrStack1[7] = 2;

   var Defsurvivors = hitarrStack2.length - r;
   var Attsurvivors = hitarrStack1.length - q;

   if(Defsurvivors >= 1)
   {
    defwinning = defwinning + Defsurvivors;
    defwincount++;
   }
   if(Attsurvivors >= 1)
   {
   
    attwinning = attwinning + Attsurvivors;
    attwincount++;
   }
   
        attdecklength = attdecklength+deck1[0].length;
        defdecklength = defdecklength+deck2[0].length;
        if(Attsurvivors == 0)
        {
            attoutline[0]++;
        }
        if(Attsurvivors == 1)
        {
            attoutline[1]++;
        }
        if(Attsurvivors == 2)
        {
            attoutline[2]++;
        }
        if(Attsurvivors == 3)
        {
            attoutline[3]++;
        }
        if(Attsurvivors == 4)
        {
            attoutline[4]++;
        }
        if(Attsurvivors == 5)
        {
            attoutline[5]++;
        }
        if(Attsurvivors == 6)
        {
            attoutline[6]++;
        }
        if(Attsurvivors == 7)
        {
            attoutline[7]++;
        }
        if(Attsurvivors == 8)
        {
            attoutline[8]++;
        }
       
       
        }
       
       
        //If army is dead give new hitpoints to all units and save a bunch of data
       
      if(q == hitarrStack1.length)
      {
        winsStack2++;
   hitarrStack1[0] = 2;

   hitarrStack1[1] = 2;
   hitarrStack1[2] = 2;
   hitarrStack1[3] = 2;
   hitarrStack1[4] = 2;
   hitarrStack1[5] = 2;
   hitarrStack1[6] = 2;
   hitarrStack1[7] = 2;

   hitarrStack2[0] = 2;
         /*
   hitarrStack2[1] = 2;

   hitarrStack2[2] = 2;
   hitarrStack2[3] = 2;
   hitarrStack2[4] = 2;
   hitarrStack2[5] = 2;
   hitarrStack2[6] = 2;
   hitarrStack2[7] = 2;

   */
   var Defsurvivors = hitarrStack2.length - r;
   var Attsurvivors = hitarrStack1.length - q;
   if(Defsurvivors >= 1)
   {
    defwinning = defwinning + Defsurvivors;
    defwincount++;
   }
   if(Attsurvivors >= 1)
   {
   
    attwinning = attwinning + Attsurvivors;
    attwincount++;
   }
   
   
        attdecklength = attdecklength+deck1[0].length;
        defdecklength = defdecklength+deck2[0].length;
       
        if(Defsurvivors == 0)
        {
            defoutline[0]++;
        }
        if(Defsurvivors == 1)
        {
            defoutline[1]++;
        }
        if(Defsurvivors == 2)
        {
            defoutline[2]++;
        }
        if(Defsurvivors == 3)
        {
            defoutline[3]++;
        }
        if(Defsurvivors == 4)
        {
            defoutline[4]++;
        }
        if(Defsurvivors == 5)
        {
            defoutline[5]++;
        }
        if(Defsurvivors == 6)
        {
            defoutline[6]++;
        }
        if(Defsurvivors == 7)
        {
            defoutline[7]++;
        }
        if(Defsurvivors == 8)
        {
            defoutline[8]++;
        }
       
        }
   }
   //alert(attwinning+", "+attwincount);
    var Attsurvivors = attwinning / attwincount;
    var Defsurvivors = defwinning / defwincount;


    var averattCards = attdecklength / CYCLES;
    var averdefCards = defdecklength / CYCLES;

   //  = defwinning / CYCLES;
   //  = attwinning / CYCLES;
   window.document.write("Attacker stack winning percentage is " + winsStack1 / CYCLES * 100 + "%. <br />Def survivors:"+Defsurvivors+" <br />Attack survivors:"+Attsurvivors+"<br />Def winnings:"+winsStack2+"");
   window.document.write("<br />Att winnings:"+winsStack1);
   window.document.write("<br />Att outline:"+attoutline);
   window.document.write("<br />Def outline:"+defoutline);
   window.document.write("<br />Attack deck unit 1:"+deck1[0]);
   window.document.write("<br />Defend deck unit 1:"+deck2[0]);
   window.document.write("<br />Att unit1 cards left:"+deck1[0].length);
   window.document.write("<br />Def unit1 cards left:"+deck2[0].length);
   window.document.write("<br />Average att cards left:"+averattCards);
   window.document.write("<br />Average def cards left:"+averdefCards);
   window.document.write("<br />Def unit1 cards left:"+deck2[0].length);
   //printf ("6 Strength Stack won %d battles with an average of %f units left\n", winsStack1, unitsLeftStack1/10000.0);
   //printf ("4 Strength Stack won %d battles with an average of %f units left\n", winsStack2, unitsLeftStack2/10000.0);
   //printf ("The 6 Strength Stack winning percentage is %f\n", (float)winsStack1/10000.0*100.0);
}

function shuffle(list) {
  var i, j, t;
  for (i = 1; i < list.length; i++) {
    j = Math.floor(Math.random()*(1+i)); 
    if (j != i) {
      t = list[i];                       
      list[i] = list[j];
      list[j] = t;
    }
  }
  return list;
}

function GetRandomInt(min, max)
{
   if (min == max || min > max)
   {
      window.alert("GetRandomInt(): Incorrect arguments!");
      return null;
   }

   return min + Math.floor(Math.random() * (max + 1 - min));
}

</script>

<div onclick="WarlordsTest();" style="width: 200px; height:100px; border:solid 1px;"></div>
User avatar
piranha
Site Admin
 
Posts: 1189
Joined: Fri Feb 12, 2010 9:44 pm

Re: Stupid or unlucky?

Postby KGB » Fri Sep 03, 2010 7:58 pm

Piranha,

Wow 48 seconds. Is your server a Commodore 64? :lol:

Thanks for posting your newest idea. I'll grab a copy of it, merge it into my program and run some tests on it sometime early next week since this isn't a huge pressing issue that has to be solved in the next day or so.

I tested quite a lot of things and the best results I got was when each unit get 3 decks that it draw card from (the card deck never ran out with 3 decks per unit)


That's a reasonable assumption though obviously eventually it will happen that a unit will run out of cards. After all a 1 strength unit has only 3 numbers it can hit on in 3 decks. So if 2 of those numbers get canceled by the enemy unit it will run out of numbers before it can make a kill (imagine 2 1 strength units fighting, the chance there is a canceled roll is 1/20 so if they do that twice they will run out of deck before they make a kill). So I hope you have a reset if the deck runs out.

When a unit killed another unit its deck would receive new cards, cards with the value 20 o basically cards that are making it more difficult to hit again. The killed units strength dived by 2 was the amount of cards given per kill so for example if a STR 4 is killed 2 new 20s in placed in the deck of the attacker.


I am not quite clear what you meant here and I have not closely looked at the code to see the answer.

When you say 2 new 20's are placed in the deck do you mean:

1) The deck is reset and 2 extra 20's are added (so there is 62 numbers instead of 60)
2) 2 extra 20's are added to what remains from the unused numbers (for example if the unit that killed the 4 str unit had used 20 of the 60 cards would it now have 22 cards for the next unit that it would have to use before it got it's deck reset).

KGB

P.S. I just looked briefly at the code. I think the answer to my question is #2. But at the same time it appears that you must be running out of decks all the time. From what I see when a unit dies and a new unit steps up he just inherits the existing deck from the dead unit.

Code: Select all
        //If attacker is killed add cards to enemy deck
         q++;
        var attdeck = deck1[q];


So sooner or later the deck reset has to be called (after 60 draws). That reset seems to add only 20 cards, not the full 60 (3 decks). So it would seem to me that once you've exhausted the initial 3 decks, you only add 1 new deck at a time. So there is much less randomness due to only adding 1 deck on the reset instead of 3. Why did you only add 1 deck at reset time?
KGB
 
Posts: 3030
Joined: Tue Feb 16, 2010 12:06 am

Re: Stupid or unlucky?

Postby piranha » Sat Sep 04, 2010 4:04 pm

Hehe yeah something like that. We like old school :mrgreen:

We are on a shared server and we will have to either move to my server or to a managed server later but its pretty nice to develop on the current one compared to my server.

I did some more tests and found that with 1v1 battles it take 2 seconds to do 1000 runs. There are so many loops within each other. Thats why it takes so much time.
The code isn't optimized at all either. I will rewrite it next week because we need to do that anyway. Its really chaotic at the moment so I will probably end up with something faster but I doubt that it will be fast enough for 1000 runs.

When you say 2 new 20's are placed in the deck do you mean:

1) The deck is reset and 2 extra 20's are added (so there is 62 numbers instead of 60)
2) 2 extra 20's are added to what remains from the unused numbers (for example if the unit that killed the 4 str unit had used 20 of the 60 cards would it now have 22 cards for the next unit that it would have to use before it got it's deck reset).


Yes #2 is right.

P.S. I just looked briefly at the code. I think the answer to my question is #2. But at the same time it appears that you must be running out of decks all the time. From what I see when a unit dies and a new unit steps up he just inherits the existing deck from the dead unit.


No take a closer look, its a multidimensional array so all units have their own 60 cards deck to pick from.

So sooner or later the deck reset has to be called (after 60 draws). That reset seems to add only 20 cards, not the full 60 (3 decks). So it would seem to me that once you've exhausted the initial 3 decks, you only add 1 new deck at a time. So there is much less randomness due to only adding 1 deck on the reset instead of 3. Why did you only add 1 deck at reset time?


I was mainly focusing on testing the spider vs Zajoman setup and the 2 hero armies and see if I could get the spider attack to go 100% win to Zajoman while still keeping the hero setup pretty close to the original chance of winning. I think the spider ran out of cards a few times and got the single deck replacement so the result wasn't 100% correct but I don't think the difference would have made any big impact with 3 new decks.

But you are right that the deck replacement needs to be the same amount of decks as you start with and not just 1 to get correct results.
I'll do some more testing too but if you come up with anything to test it would be great.
User avatar
piranha
Site Admin
 
Posts: 1189
Joined: Fri Feb 12, 2010 9:44 pm

Re: Stupid or unlucky?

Postby Zajoman » Sat Sep 04, 2010 6:22 pm

Unless you're calculating trajectories through multidimensional warp rifts in space, I have no idea how that simulation can take up so much time. I noticed that when two units clash (particularly when both are low strength), sometimes it takes even 15 or more rolls for one hit to happen, because the chance is so low. The roll must be less or equal to the strength and the opponent must miss, and that in some certain strength combinations adds a huge number of rolls. How about trying to make it like W4 appears to work. There is simply a table of strength differences and each difference has a percentage chance for the stronger unit to hit. To make myself clear, when a 3 strength unit goes against a 1 strength unit, the difference is 2 and this number has a predefined chance for the stronger unit to hit. Let's say for the difference of 1 point it's 70%, for 2 it's 78%, for 3 it's 85%, etc. (I'm making these numbers up, it doesn't matter for the example). Of course, when the stronger unit misses, the weaker unit hits. Now the program does only one single roll to find out who hits. So a clash between two 2 HP units will always take at most 3 rolls, no matter what their strengths are. So the number of rolls for a big battle will be considerably reduced, in my opinion. And then I simply cannot understand how that could take more than a few milliseconds to compute in 1000 loops, and so you could use the 90% cut off method.
Zajoman
 
Posts: 107
Joined: Thu Aug 12, 2010 6:56 pm

Re: Stupid or unlucky?

Postby piranha » Sun Sep 05, 2010 3:21 pm

Sounds pretty good to use that model. It would speed things up.

I came to think about another idea when I read your post.
If there is some kind of weight system. Lets take your spider attack for example.

Lets say there is also a predefined set of points per strength difference. If the STR 9 killing the STR 4 is worth 2 points for example the spider side would accumulate points for each kill and if it reaches a certain value the battle will simply restart.

It could be something like defender total STR / number of defender - attacker total STR / number of attackers
You would get the total STR difference and from that set a point limit for each side that will cause the battle to restart. If one side reach its point limit you know that the rolls have been to good for one side.

Wouldn't a system like that do the job without having to run a lot of simulation runs?
User avatar
piranha
Site Admin
 
Posts: 1189
Joined: Fri Feb 12, 2010 9:44 pm

Re: Stupid or unlucky?

Postby KGB » Sun Sep 05, 2010 4:10 pm

Piranha,

That kind of system might work. But you'd have to do a lot of experiments to get the weights right for all kinds of stacks sizes / strengths etc. I suspect it will be an awful lot of work to get it to accurately model a 90% rule in all cases.

None of that of course addresses the whole issue of the first strike (assassin) skill. With Ghosts in play, one side might easily exceed it's kill numbers due to first strike and that would be a legitimate result.

I have another question. Can your php program call/run another program? I mean the C++ code simulator I wrote takes about 1 ms to run 10000 trials. If you wrote a small C++ app that might be all you need to do the simulation 'live'. All you'd pass to the C++ app are the stacks on both sides after all bonus's were calculated. The C++ code would simply run 10000 trials and return the 90% cut off point. Then the php code you've already got in place can do the real combat and as long as it falls outside the range you run again until it does.

KGB
KGB
 
Posts: 3030
Joined: Tue Feb 16, 2010 12:06 am

Re: Stupid or unlucky?

Postby piranha » Mon Sep 06, 2010 4:11 pm

You are right. I tried today to create a system like that today. It turned out to be hard and probably not the right way to do it.

It is possible to send data between PHP and c++ but my experience is that its a nightmare to get things like that to work. It could be a solution but right now I have hope for another solution.

I told my brother about the problem and he says it can be calculated instead of simulated. I don't know to do it but he is going to take a look at it.
User avatar
piranha
Site Admin
 
Posts: 1189
Joined: Fri Feb 12, 2010 9:44 pm

Re: Stupid or unlucky?

Postby Zajoman » Mon Sep 06, 2010 5:51 pm

I've been fiddling with this in PHP, trying out my proposed method, and I can say that the mt_rand() function really seems to give numbers in sequences rather than well distributed. I've finished the first draft of the simulator, for now without all the bonuses, criticals etc., but those will only add a veeeery little caluclation time in the beginning of a battle, and only once if I rework the code a bit.

1000 loops takes 0.31 seconds here, but I really believe that even 100 loops gives us enough ensurance, which takes 0.03 seconds.
Zajoman
 
Posts: 107
Joined: Thu Aug 12, 2010 6:56 pm

Re: Stupid or unlucky?

Postby KGB » Mon Sep 06, 2010 7:57 pm

Piranha,

Good luck to your brother!

I can be calculated but I bet it's much harder than he thinks. Doing 1v1 unit is pretty easy of course and even up to 2v2 isn't bad. But doing stacks v stacks is much harder in a pure math sense. That's why it's easier to just simulate it.

Zajoman,

I think you need at least 1000 loops. Something that only occurs 10% of the time might not occur enough in 100 tries to get accurately modeled (ie 12% could easily only occur 8 times making you think it's outside the 90% when it's inside the 90%). But at 1000 loops it should happen 100 times so that's enough to be sure it happens 10% of the time.

KGB
KGB
 
Posts: 3030
Joined: Tue Feb 16, 2010 12:06 am

Re: Stupid or unlucky?

Postby piranha » Tue Sep 07, 2010 5:48 am

Zajo: There are some things you need to take into consideration compared to the javascript simulator.
The STR values must be dynamic for each attack, when a unit with anti air bonus steps up vs a flyer he should get his bonus and then it should not be there as soon as he is facing a ground unit again.
All the bonuses and actual STR values must be calculated on each round. We might add magic or items that require dynamic STR later too.

Do you think setting an array to 1-20 and then shuffle would give better results than mt_rand?

I think the idea you talked about just have a predefined values based on the STR dif should help. It could easily remove 75% of the dice throwing part. The big slowdown is probably dealing with the dynamic values in my current version.

KGB: Okay, I will give him a chance to look at it. He is really good at that type of stuff but if it gets to complicated with many units it might not be a good solution either.
User avatar
piranha
Site Admin
 
Posts: 1189
Joined: Fri Feb 12, 2010 9:44 pm

PreviousNext

Return to Game discussion

Who is online

Users browsing this forum: No registered users and 16 guests

cron
Not able to open ./cache/data_global.php