Link to home
Start Free TrialLog in
Avatar of Lolly-Ink
Lolly-InkFlag for Australia

asked on

Royal Flush Browser Test

I wrote a JavaScript program to calculate poker statistics for fun. When it came time to find out how often a Royal Flush can occur, I ran it IE8 RC1, but I would nearly always get an exception when dealing 60,000 hands or so (see code snippet bottom comment).

So I tried the program in Google Chrome 2.0.166.1, and time after time there was no problem at all. It performs the best compared to all other browsers.

After a short analysis, it seems that IE8 RC1 is writing values to arrays which are then passed to the "hand" function in my code, and then losing the data in the arrays somehow ... the array items become undefined values.

What gives?

PS: Royal Flush seems to occur once in every 4000 hands dealt on average. As long as you don't fold of course.
<html>
<style>
   body, td
   {
      font-size: 8px;
      font-family: courier new;
   }
 
   td
   {
      padding: 10px;
      border: 1px solid black;
   }
</style>
<script>
   function getRandom(max)
   {
      return Math.min(max, Math.floor(Math.random() * (max + 1)));
   }
 
   var td = 0;
   var _c = ["2", "3", "4", "5", "6", "7", "8", "9", "T", "J", "Q", "K", "A"];
   var _s = ["\u2666", "\u2660", "\u2663", "\u2665"];
   var _h = ["_H", "_P", "2P", "_3", "ST", "FL", "FH", "_4", "SF"];
   var _attempts = 0;
   var _rf = false;
   function deal()
   {
      var e = document.getElementById("td" + td);
      if (!e) return;
      var inn = "___________SUM__2x__ST__FL__H<br><br>";
      var p = [];
      var b = [];
      var d = [];
      for (var i = 0; i < 5; i++)
      {
         do
         {
            var v = getRandom(_c.length - 1);
            var w = getRandom(_s.length - 1);
            var cc = _c[v] + _s[w];
            if (d.join("").indexOf(cc) == -1)
            {
               d.push(cc);
               b.push(cc);
               break;
            }
         }
         while (true);//(b.length == i);
      }
      var rf = false;
      var sf = false;
      var four = false;
      var fh = false;
      var fl = false;
      var st = false;
      var three = false;
      var p2 = false;
      var pair = false;
      var high = false;
      for (var m = 0; m < 9; m++)
      {
         p[m] = [];
         var vv = [];
         var ww = [];
         for (var i = 0; i < 2; i++)
         {
            do
            {
               var v = getRandom(_c.length - 1);
               var w = getRandom(_s.length - 1);
               var cc = _c[v] + _s[w];
               if (d.join("").indexOf(cc) == -1)
               {
                  d.push(cc);
                  p[m].push(cc);
                  vv.push(v + 2);
                  ww.push(w);
                  break;
               }
            }
            while (true);//(p[m].length == i);
         }
         var _hand = hand(p[m], b);
         if (_hand == "SF")
         {
            sf = true;
            if (_rf)
            {
               rf = true;
            }
         }
         if (_hand == "_4")
         {
            four = true;
         }
         if (_hand == "FH")
         {
            fh = true;
         }
         if (_hand == "FL")
         {
            fl = true;
         }
         if (_hand == "ST")
         {
            st = true;
         }
         if (_hand == "_3")
         {
            three = true;
         }
         if (_hand == "2P")
         {
            p2 = true;
         }
         if (_hand == "_P")
         {
            pair = true;
         }
         if (_hand == "_H")
         {
            high = true;
         }
         inn += (m + 1) + ". " + p[m].join(" ") + "___" + 
            (String(100 + vv[0] + vv[1]).substr(1)) + "___" + 
            ((vv[0] == vv[1]) ? "*" : "_") + "___" + 
            ((Math.abs(vv[0] - vv[1]) == 1 || Math.abs(vv[0] - vv[1]) == 12) ? "*" : "_") + "___" + 
            ((ww[0] == ww[1]) ? "*" : "_") + "__" + 
            _hand;
         inn += "<br><br>";
      }
      inn += b.join(" ");
      document.getElementById("divAttempts").innerHTML = ++_attempts;
      if (rf)// && !(pair || p2 || three || st || fl || fh || four || sf))
      {
         e.innerHTML = inn;
         td++;
      }
      window.setTimeout("deal()", 0);
   }
 
   var ___hand = {h: 0, p: 1, p2: 2, three: 3, st: 4, fl: 5, fh: 6, four: 7, sf: 8};
   function hand(__p, __b)
   {
      var __hand = ___hand.h;
      var h7 = __p.concat(__b);
      var sf = false;
      var four = false;
      var fh = false;
      var fl = false;
      var st = false;
      var three = false;
      var p2 = false;
      var pair = false;
      var h = -1;
      var ac = [];
      var as = [];
      var acs = [];
      var pp = 0;
      var __is;
      for (var i = 0; i < 7; i++)
      {
         var c;
         try
         {
            c = h7[i].substr(0, 1);
         }
         catch (__exception)
         {
            throw __exception;
         }
         var s = h7[i].substr(1, 1);
         var ic = _c.join("").indexOf(c);
         if (h < ic)
         {
            h = ic;
         }
         ac[ic] = (ac[ic] ? (ac[ic] + 1) : 1);
         if (!acs[ic])
         {
            acs[ic] = [];
         }
         acs[ic].push(s);
         switch (ac[ic])
         {
            case 2:
               pair = true;
               __hand = Math.max(__hand, ___hand.p);
               pp++;
               if (pp == 2)
               {
                  p2 = true;
                  __hand = Math.max(__hand, ___hand.p2);
               }
               if (three)
               {
                  fh = true;
                  __hand = Math.max(__hand, ___hand.fh);
               }
               break;
            case 3:
               three = true;
               __hand = Math.max(__hand, ___hand.three);
               if (pp == 2)
               {
                  fh = true;
                  __hand = Math.max(__hand, ___hand.fh);
               }
               break;
            case 4:
               four = true;
               __hand = Math.max(__hand, ___hand.four);
         }
         var is = _s.join("").indexOf(s);
         as[is] = (as[is] ? (as[is] + 1) : 1);
         switch (as[is])
         {
            case 5:
               fl = true;
               __hand = Math.max(__hand, ___hand.fl);
               __is = is;
               break;
         }
      }
      var run = 0;
      var st_low = false;
      for (var i = 0; i < ac.length; i++)
      {
         if (ac[i])
         {
            run++;
            if (run >= 5)
            {
               st = true;
               __hand = Math.max(__hand, ___hand.st);
               break;
            }
         }
         else
         {
            if ((run == 4) && (i == 4) && ac[ac.length - 1])
            {
               st = true;
               __hand = Math.max(__hand, ___hand.st);
               st_low = true;
               break;
            }
            else
            {
               run = 0;
            }
         }
      }
      _rf = false;
      if (fl && st)
      {
         run = 0;
         var runfl = 0;
         _actualHand = [];
         for (var i = 0; i < ac.length; i++)
         {
            if (ac[i])
            {
               run++;
               if ((acs[i][0] === _s[__is]) || (acs[i][1] === _s[__is]) || (acs[i][2] === _s[__is]))
               {
                  runfl++;
                  if (runfl >= 5)
                  {
                     sf = true;
                     __hand = Math.max(__hand, ___hand.sf);
                     _rf = ((i == (_c.length - 1)) && !st_low);
                     break;
                  }
               }
               else
               {
                  runfl = 0;
               }
            }
            else
            {
               if ((runfl == 4) && st_low && ((acs[ac.length - 1][0] === _s[__is]) || (acs[ac.length - 1][1] === _s[__is]) || (acs[ac.length - 1][2] === _s[__is])))
               {
                  sf = true;
                  __hand = Math.max(__hand, ___hand.sf);
                  break;
               }
               else
               {
                  run = 0;
                  runfl = 0;
               }
            }
         }
      }
      return _h[__hand];
   }
</script>
<body onload="deal()">
<div id="divAttempts"></div>
<table>
<tr>
   <td id="td0"></td>
   <td id="td1"></td>
   <td id="td2"></td>
   <td id="td3"></td>
</tr>
<tr>
   <td id="td4"></td>
   <td id="td5"></td>
   <td id="td6"></td>
   <td id="td7"></td>
</tr>
<tr>
   <td id="td8"></td>
   <td id="td9"></td>
   <td id="td10"></td>
   <td id="td11"></td>
</tr>
</table>
</body>
</html>
 
 
<!--
Webpage error details
 
User Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; SU 3.14; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; Maxthon; .NET CLR 2.0.50727; FDM; .NET CLR 3.0.04506.30; .NET CLR 3.0.04506.648; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)
Timestamp: Tue, 3 Mar 2009 04:50:30 UTC
 
 
Message: 'undefined' is null or not an object
Line: 167
Char: 13
Code: 0
URI: file:///D:/Temp/Target/Strange.htm
-->

Open in new window

Avatar of Badotz
Badotz
Flag of United States of America image

Chrome uses a different (better? faster?) JavaScript engine than IE8 - perhaps this accounts for the discrepancy?
Also, there is no <head> section?
This from JSLint (http://www.jslint.com/):

Error:

Implied global: _actualHand 245, document 14, window 125

Problem at line 15 character 15: Expected '{' and instead saw 'return'.

if (!e) return;

Problem at line 51 character 21: 'i' is already defined.

for (var i = 0; i < 2; i++)

Problem at line 55 character 22: 'v' is already defined.

var v = getRandom(_c.length - 1);

Problem at line 56 character 22: 'w' is already defined.

var w = getRandom(_s.length - 1);

Problem at line 57 character 23: 'cc' is already defined.

var cc = _c[v] + _s[w];

Problem at line 129 character 17: 'hand' was used before it was defined.

function hand(__p, __b)

Problem at line 202 character 10: This 'switch' should be an 'if'.

switch (as[is])

Problem at line 213 character 18: 'i' is already defined.

for (var i = 0; i < ac.length; i++)

Problem at line 246 character 21: 'i' is already defined.

for (var i = 0; i < ac.length; i++)
Avatar of Lolly-Ink

ASKER

Thanks. I applied the changes suggested by JSLint, but IE8 RC1 still throws an exception. The suggestions were only cosmetic.

I'd be interested to know if anyone running IE8 RC1 with the code encounters the same error.

I'd be interested to hear from the Microsoft IE team whether the array phenomenon is a known bug in IE8 RC1. But I doubt the problem will be noticed since there is no easy feedback option in the menu, unlike Google Chrome.

If you analyse the code, it is impossible to call the "hand" function with anything other than 2 arrays, with 2 elements and 5 elements respectively, and each element containing a 2 character string. Yet IE8 RC1 manages to deliver undefined elements on the odd occasion (e.g. after 100,000 random hands dealt).
Here's an update:

I dealt 653,000 hands in Google Chrome - no errors, very fast, low CPU.

I dealt 1,014,000 hands in IE7 - no errors, slow (but opened 10 windows to make up for it), low CPU.

I dealt 278,000 hands in IE8 RC1 - 2 errors, slow, average CPU.

So the IE team have introduced a bug in their latest version.
Have you tried it in Firefox or Safari?

I know the Chrome JS engine is much faster than IE, not sure if it is faster than FF or Safari.
I did try it in all browsers initially, but my interest focused on Chrome and IE8 RC1.
OK. I've installed the final version of IE8. Let's see if they have fixed this month-old critical problem which I reported here and they ignored/flamed me on their forums...

Testing in progress ...
Lookout for a new video coming soon called "IE8 Fail".

I dealt 65034 hands in IE8 (final) - got 1 error. No need to test further.

I'll test IE8 again when they get around to releasing the next update according to their lame update schedule - 6 months? 12 months? I don't know. We'll see.

Reported error:
Webpage error details

User Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; SU 3.14; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; Maxthon; .NET CLR 2.0.50727; FDM; .NET CLR 3.0.04506.30; .NET CLR 3.0.04506.648; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)
Timestamp: Tue, 7 Apr 2009 23:22:02 UTC


Message: String expected
Line: 73
Char: 16
Code: 0
URI: file:///D:/Temp/Target/Strange.htm
I have written simpler code (below) to reproduce the same issue. Basically Internet Explorer is corrupting memory of arrays passed to functions.

Tests for this example reproduce corruption after anywhere between 50,000 and 150,000 calls to the passArray function.
<html>
<style>
   table, th, td
   {
      border: 1px solid black;
      padding: 5px;
   }
 
   table
   {
      width: 100%;
   }
 
   th, td
   {
      width: 50%;
   }
</style>
<script>
   var _attempts = 0;
   var _corrupt  = false;
   var __x       = [];
 
   function testArray()
   {
      doPassArray();
      document.getElementById("spanAttempts").innerHTML = ++_attempts;
      if (_corrupt)
      {
         document.getElementById("tdResultCorrupt").innerHTML = __x.join("<br>");
         __x = [];
         doPassArray();
         document.getElementById("tdResultNext").innerHTML = __x.join("<br>");
      }
      else
      {
         if ((_attempts % 1000) == 0)
         {
            window.setTimeout("testArray()", 0);
         }
         else
         {
            testArray();
         }
      }
   }
 
   function doPassArray()
   {
      passArray(
         ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"],
         ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], 
         ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], 
         ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"],
         ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], 
         ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], 
         ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"],
         ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], 
         ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], 
         ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"], ["aa", "bb"]);
   }
 
   function passArray()
   {
      for (var indexArgument = 0; (indexArgument < arguments.length) && !_corrupt; indexArgument++)
      {
         for (var index = 0; index < arguments[indexArgument].length; index++)
         {
            if (arguments[indexArgument][index] == undefined)
            {
               _corrupt = true;
               break;
            }
         }
      }
      if (_corrupt)
      {
         for (var indexArgument = 0; indexArgument < arguments.length; indexArgument++)
         {
            __x.push("argument " + indexArgument);
            for (var item in arguments[indexArgument])
            {
               __x.push(item + ": " + arguments[indexArgument][item]);
            }
            __x.push("");
         }
      }
   }
</script>
<body onload="testArray()">
   Internet Explorer - Array Corruption Attempts ... <span id="spanAttempts"></span><br>
   <table>
      <tr>
         <th>Corrupt</th>
         <th>Next (Back To Normal?)</th>
      </tr>
      <tr>
         <td id="tdResultCorrupt">&nbsp;</td>
         <td id="tdResultNext">&nbsp;</td>
      </tr>
   </table>
</body>
</html>

Open in new window

With only minor changes - changing the values in the array, adding a <head> section and a title - the page ran through 131,072 iterations before failing.

I'm wondering if the reliance on the arguments object - instead of a named parameter - in the "passArray" function has anything to do with this? Even though you pass a "true" array, the arguments object is not really an array, although it has similar properties. I'm going to try it again and I'll post my findings in a while.
<html>
<head>
<title>IE8 Pass Array Test</title>
<style>
table, th, td {
	border: 1px solid black;
	padding: 5px;
}
table {
	width: 100%;
}
 
th, td {
	width: 50%;
}
</style>
<script>
   var _attempts = 0;
   var _corrupt  = false;
   var __x       = [];
 
   function passArray() {
      for (var indexArgument = 0; (indexArgument < arguments.length) && !_corrupt; indexArgument++) {
         for (var index = 0; index < arguments[indexArgument].length; index++) {
            if (arguments[indexArgument][index] === undefined) {
               _corrupt = true;
               break;
            }
         }
      }
      if (_corrupt) {
         for (var indexArgument = 0; indexArgument < arguments.length; indexArgument++) {
            __x.push("argument " + indexArgument);
            for (var item in arguments[indexArgument]) {
               __x.push(item + ": " + arguments[indexArgument][item]);
            }
            __x.push("");
         }
      }
   }
 
   function doPassArray() {
      passArray(
         ["aa", "bb"], ["ba", "cb"], ["0a", "ab"], ["1a", "be"], ["2a", "qb"], ["a3", "bs"], ["aq", "0b"], ["a1", "ub"], 
         ["ab", "bc"], ["ca", "db"], ["0a", "bb"], ["1a", "bf"], ["2a", "rb"], ["a3", "bt"], ["ap", "1b"], ["a0", "bt"], 
         ["ac", "bd"], ["da", "eb"], ["0a", "cb"], ["1a", "bg"], ["2a", "sb"], ["a3", "bu"], ["ao", "2b"], ["9a", "bz"], 
         ["ad", "be"], ["ea", "fb"], ["0a", "db"], ["1a", "bh"], ["2a", "tb"], ["a3", "bv"], ["an", "3b"], ["8a", "by"], 
         ["ae", "bf"], ["fa", "gb"], ["0a", "eb"], ["1a", "bi"], ["2a", "ub"], ["a3", "bw"], ["am", "4b"], ["7a", "bx"], 
         ["af", "bg"], ["ga", "hb"], ["0a", "fb"], ["1a", "bj"], ["2a", "vb"], ["a3", "bx"], ["al", "5b"], ["6a", "bw"], 
         ["ag", "bh"], ["ha", "ib"], ["0a", "gb"], ["1a", "bk"], ["2a", "wb"], ["a3", "by"], ["ak", "6b"], ["5a", "bv"], 
         ["ah", "bi"], ["ia", "jb"], ["0a", "hb"], ["1a", "bl"], ["2a", "xb"], ["a3", "bz"], ["aj", "7b"], ["4a", "bu"], 
         ["ai", "bj"], ["ja", "kb"], ["0a", "ib"], ["1a", "bm"], ["2a", "yb"], ["a3", "ba"], ["ai", "8b"], ["3a", "bt"], 
         ["aj", "bk"], ["ka", "lb"], ["0a", "jb"], ["1a", "bn"], ["2a", "zb"], ["a3", "bb"], ["ah", "9b"], ["2a", "bs"], 
         ["ak", "bl"], ["la", "mb"], ["0a", "kb"], ["1a", "bo"], ["2a", "ab"], ["a3", "bc"], ["ag", "b0"], ["1a", "br"], 
         ["al", "bm"], ["ma", "nb"], ["0a", "lb"], ["1a", "bp"], ["2a", "bb"], ["a3", "bd"], ["af", "b1"], ["0a", "bq"], 
         ["am", "bn"], ["na", "ob"], ["0a", "mb"], ["1a", "bq"]);
   }
 
   function testArray() {
      doPassArray();
      document.getElementById("spanAttempts").innerHTML = ++_attempts;
      if (_corrupt) {
         document.getElementById("tdResultCorrupt").innerHTML = __x.join("<br>");
         __x = [];
         doPassArray();
         document.getElementById("tdResultNext").innerHTML = __x.join("<br>");
      }
      else {
         if ((_attempts % 1000) == 0) {
            window.setTimeout("testArray()", 0);
         }
         else {
            testArray();
         }
      }
   }
</script>
</head>
<body onload="testArray()">
   Internet Explorer - Array Corruption Attempts ... <span id="spanAttempts"></span><br>
   <table>
      <tr>
         <th>Corrupt</th>
         <th>Next (Back To Normal?)</th>
      </tr>
      <tr>
         <td id="tdResultCorrupt">&nbsp;</td>
         <td id="tdResultNext">&nbsp;</td>
      </tr>
   </table>
</body>
</html>

Open in new window

Nope - it failed at the same spot afer 131,072 iterations.
ASKER CERTIFIED SOLUTION
Avatar of Badotz
Badotz
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
The following response is to be read calmly, as I am. There is no ill-emotion intended:

"there are no problems passing arrays anymore in IE8"

How can you say that? From the beginning of this EE question it has been shown that parameters are being corrupted. It may or may not be peculiar to array parameters. I don't know. But there is still a problem with IE8's handling of JavaScript function parameters. And it interests me greatly because at my work we develop real-time high-end web-based applications that run in IE8, and we can't afford them falling over down the track due to this fart-in-an-elevator problem that nobody in the IE development team choose to own up to.
Problem has not been solved. I am still keenly interested in a solution.
Problem still exists. Question is still valid.
Thankyou for helping me to refine a test-case for reproducing this error.
Thanks; you are too generous.
modEEdah

Thanks for the advice. There are 5 options under "How do I close a question?". None of them were suitable in my case, and I didn't want to delete the question: people should know about this problem and be able to do a Google search and find it.

I needed to ask a new question, and was prevented from doing so: therefore I was forced into choosing one of the 5 options. I think the option I chose was appropriate enough.

Perhaps you could have a 6th option for questions which have no solution as yet.
That helps, thanks.

Although in this case I think those avenues had been exhausted. The answer to the question is that there is a memory corruption issue in IE8 that has not been acknowledged or fixed by the MS society yet.