Add a Unicode test to RexBench that matches non-BMP characters
authormsaboff@apple.com <msaboff@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 18 Aug 2017 00:56:59 +0000 (00:56 +0000)
committermsaboff@apple.com <msaboff@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 18 Aug 2017 00:56:59 +0000 (00:56 +0000)
https://bugs.webkit.org/show_bug.cgi?id=175697

Reviewed by JF Bastien.

Added a new sub test that simulates 5 card stud poker.  This test uses the Unicode playing
cards code points, U+1F0A1..U+1F0DE, as the card representation.  The scoring of hands is
done using three regular expressions, one to check for a flush, one to check for straights
and one to check for pairs, three of a kind and four of a kind.

* RexBench/UniPoker: Added.
* RexBench/UniPoker/benchmark.js: Added.
(UniPokerBenchmark):
(UniPokerBenchmark.prototype.setup.):
(UniPokerBenchmark.prototype.setup.Math.random):
(UniPokerBenchmark.prototype.setup):
(UniPokerBenchmark.prototype.runOnce):
(UniPokerBenchmark.prototype.validate):
* RexBench/UniPoker/expected.js: Added.
(PlayerExpectation):
(PlayerExpectation.prototype.validate):
* RexBench/UniPoker/poker.js: Added.
(CardDeck):
(CardDeck.prototype.newDeck):
(CardDeck.prototype.shuffle):
(CardDeck.prototype.dealOneCard):
(CardDeck.cardRank):
(CardDeck.cardName):
(Hand):
(Hand.prototype.clear):
(Hand.prototype.takeCard):
(Hand.prototype.score):
(Hand.prototype.get rank):
(Hand.prototype.toString):
(Player):
(Player.prototype.scoreHand):
(Player.prototype.wonHand):
(Player.prototype.get name):
(Player.prototype.get hand):
(Player.prototype.get wins):
(Player.prototype.get handTypeCounts):
(playHands):
* RexBench/about.html:
* RexBench/cli.js:
* RexBench/glue.js:
(driver.reportResult):
* RexBench/index.html:
* RexBench/unipoker_benchmark.js: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@220893 268f45cc-cd09-0410-ab3c-d52691b4dbfc

PerformanceTests/ChangeLog
PerformanceTests/RexBench/UniPoker/benchmark.js [new file with mode: 0644]
PerformanceTests/RexBench/UniPoker/expected.js [new file with mode: 0644]
PerformanceTests/RexBench/UniPoker/poker.js [new file with mode: 0644]
PerformanceTests/RexBench/about.html
PerformanceTests/RexBench/cli.js
PerformanceTests/RexBench/glue.js
PerformanceTests/RexBench/index.html
PerformanceTests/RexBench/unipoker_benchmark.js [new file with mode: 0644]

index 7006481..84273f9 100644 (file)
@@ -1,3 +1,54 @@
+2017-08-17  Michael Saboff  <msaboff@apple.com>
+
+        Add a Unicode test to RexBench that matches non-BMP characters
+        https://bugs.webkit.org/show_bug.cgi?id=175697
+
+        Reviewed by JF Bastien.
+
+        Added a new sub test that simulates 5 card stud poker.  This test uses the Unicode playing
+        cards code points, U+1F0A1..U+1F0DE, as the card representation.  The scoring of hands is
+        done using three regular expressions, one to check for a flush, one to check for straights
+        and one to check for pairs, three of a kind and four of a kind.
+
+        * RexBench/UniPoker: Added.
+        * RexBench/UniPoker/benchmark.js: Added.
+        (UniPokerBenchmark):
+        (UniPokerBenchmark.prototype.setup.):
+        (UniPokerBenchmark.prototype.setup.Math.random):
+        (UniPokerBenchmark.prototype.setup):
+        (UniPokerBenchmark.prototype.runOnce):
+        (UniPokerBenchmark.prototype.validate):
+        * RexBench/UniPoker/expected.js: Added.
+        (PlayerExpectation):
+        (PlayerExpectation.prototype.validate):
+        * RexBench/UniPoker/poker.js: Added.
+        (CardDeck):
+        (CardDeck.prototype.newDeck):
+        (CardDeck.prototype.shuffle):
+        (CardDeck.prototype.dealOneCard):
+        (CardDeck.cardRank):
+        (CardDeck.cardName):
+        (Hand):
+        (Hand.prototype.clear):
+        (Hand.prototype.takeCard):
+        (Hand.prototype.score):
+        (Hand.prototype.get rank):
+        (Hand.prototype.toString):
+        (Player):
+        (Player.prototype.scoreHand):
+        (Player.prototype.wonHand):
+        (Player.prototype.get name):
+        (Player.prototype.get hand):
+        (Player.prototype.get wins):
+        (Player.prototype.get handTypeCounts):
+        (playHands):
+        * RexBench/about.html:
+        * RexBench/cli.js:
+        * RexBench/glue.js:
+        (driver.reportResult):
+        * RexBench/index.html:
+        * RexBench/unipoker_benchmark.js: Added.
+
 2017-08-16  Mathias Bynens  <mathias@qiwi.be>
 
         Speedometer: Update Angular 1 TodoMVC example to v1.6.5
diff --git a/PerformanceTests/RexBench/UniPoker/benchmark.js b/PerformanceTests/RexBench/UniPoker/benchmark.js
new file mode 100644 (file)
index 0000000..a327992
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+"use strict";
+
+class UniPokerBenchmark extends Benchmark {
+    constructor(verbose = 0)
+    {
+        super(verbose);
+        this._players = [];
+    }
+
+    setup()
+    {
+        Math.random = (function() {
+            var seed = 49734321;
+            return function() {
+                // Robert Jenkins' 32 bit integer hash function.
+                seed = ((seed + 0x7ed55d16) + (seed << 12))  & 0xffffffff;
+                seed = ((seed ^ 0xc761c23c) ^ (seed >>> 19)) & 0xffffffff;
+                seed = ((seed + 0x165667b1) + (seed << 5))   & 0xffffffff;
+                seed = ((seed + 0xd3a2646c) ^ (seed << 9))   & 0xffffffff;
+                seed = ((seed + 0xfd7046c5) + (seed << 3))   & 0xffffffff;
+                seed = ((seed ^ 0xb55a4f09) ^ (seed >>> 16)) & 0xffffffff;
+                return (seed & 0xfffffff) / 0x10000000;
+            };
+        })();
+
+        this._players = [];
+        this._players.push(new Player("Player 1"));
+        this._players.push(new Player("Player 2"));
+        this._players.push(new Player("Player 3"));
+        this._players.push(new Player("Player 4"));
+    }
+
+    runOnce()
+    {
+        playHands(this._players);
+    }
+
+    validate()
+    {
+        if (this._players.length != playerExpectations.length)
+            throw "Expect " + playerExpectations.length + ", but actually have " + this._players.length;
+
+        for (let playerIdx = 0; playerIdx < playerExpectations.length; playerIdx++)
+            playerExpectations[playerIdx].validate(this._players[playerIdx]);
+    }
+}
diff --git a/PerformanceTests/RexBench/UniPoker/expected.js b/PerformanceTests/RexBench/UniPoker/expected.js
new file mode 100644 (file)
index 0000000..d3c2ac2
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+"use strict";
+
+class PlayerExpectation
+{
+    constructor(wins, handTypeCounts)
+    {
+        this._wins = wins;
+        this._handTypeCounts = handTypeCounts;
+    }
+
+    validate(player)
+    {
+        if (player.wins != this._wins)
+            throw "Expected " + player.name + " to have " + this._wins + ", but they have " + player.wins;
+
+        let actualHandTypeCounts = player.handTypeCounts;
+        if (this._handTypeCounts.length != actualHandTypeCounts.length)
+            throw "Expected " + player.name + " to have " + this._handTypeCounts.length + " hand types, but they have " + actualHandTypeCounts.length;
+
+        for (let handTypeIdx = 0; handTypeIdx < this._handTypeCounts.length; handTypeIdx++) {
+            if (this._handTypeCounts[handTypeIdx] != actualHandTypeCounts[handTypeIdx]) {
+                throw "Expected " + player.name + " to have " + this._handTypeCounts[handTypeIdx] + " " + this._handTypes[handTypeIdx] + " hands, but they have " + actualHandTypeCounts[handTypeIdx];
+            }
+        }
+    }
+}
+
+PlayerExpectation._handTypes = [
+    "High Cards",
+    "Pairs",
+    "Two Pairs",
+    "Three of a Kinds",
+    "Straights",
+    "Flushes",
+    "Full Houses",
+    "Four of a Kinds",
+    "Straight Flushes",
+    "Royal Flushes"
+];
+    
+var playerExpectations = [];
+
+playerExpectations.push(new PlayerExpectation(9944, [ 20065, 16861, 1875, 898, 161, 75, 58, 7, 0, 0]));
+playerExpectations.push(new PlayerExpectation(9969, [ 20142, 16782, 1891, 918, 131, 61, 60, 13, 2, 0]));
+playerExpectations.push(new PlayerExpectation(10028, [ 20123, 16816, 1936, 828, 152, 74, 54, 16, 1, 0]));
+playerExpectations.push(new PlayerExpectation(10062, [ 20082, 16801, 1933, 874, 169, 70, 61, 9, 1, 0]));
diff --git a/PerformanceTests/RexBench/UniPoker/poker.js b/PerformanceTests/RexBench/UniPoker/poker.js
new file mode 100644 (file)
index 0000000..96df0be
--- /dev/null
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+"use strict";
+
+
+class CardDeck
+{
+    constructor()
+    {
+        this.newDeck();
+    }
+
+    newDeck()
+    {
+        // Make a shallow copy of a new deck
+        this._cards = CardDeck._newDeck.slice(0);
+    }
+
+    shuffle()
+    {
+        this.newDeck();
+
+        for (let index = 52; index !== 0;) {
+            // Select a random card
+            let randomIndex = Math.floor(Math.random() * index);
+            index--;
+
+            // Swap the current card with the random card
+            let tempCard = this._cards[index];
+            this._cards[index] = this._cards[randomIndex];
+            this._cards[randomIndex] = tempCard;
+        }
+    }
+
+    dealOneCard()
+    {
+        return this._cards.shift();
+    }
+
+    static cardRank(card)
+    {
+        // This returns a numeric value for a card.
+        // Ace is highest.
+
+        let rankOfCard = card.codePointAt(0) & 0xf;
+        if (rankOfCard == 0x1) // Make Aces higher than Kings
+            rankOfCard = 0xf;
+
+        return rankOfCard;
+    }
+
+    static cardName(card)
+    {      
+        if (typeof(card) == "string")
+            card = card.codePointAt(0);
+        return this._rankNames[card & 0xf];
+    }
+}
+
+CardDeck._rankNames = [
+    "", "Ace", "2", "3", "4", "5", "6", "7", "8", "9", "10", "Jack", "", "Queen", "King"
+];
+
+CardDeck._newDeck = [
+    // Spades
+    "\u{1f0a1}", "\u{1f0a2}",  "\u{1f0a3}",  "\u{1f0a4}",  "\u{1f0a5}",
+    "\u{1f0a6}", "\u{1f0a7}",  "\u{1f0a8}",  "\u{1f0a9}",  "\u{1f0aa}",
+    "\u{1f0ab}", "\u{1f0ad}",  "\u{1f0ae}",
+    // Hearts
+    "\u{1f0b1}", "\u{1f0b2}",  "\u{1f0b3}",  "\u{1f0b4}",  "\u{1f0b5}",
+    "\u{1f0b6}", "\u{1f0b7}",  "\u{1f0b8}",  "\u{1f0b9}",  "\u{1f0ba}",
+    "\u{1f0bb}", "\u{1f0bd}",  "\u{1f0be}",
+    // Clubs
+    "\u{1f0d1}", "\u{1f0d2}",  "\u{1f0d3}",  "\u{1f0d4}",  "\u{1f0d5}",
+    "\u{1f0d6}", "\u{1f0d7}",  "\u{1f0d8}",  "\u{1f0d9}",  "\u{1f0da}",
+    "\u{1f0db}", "\u{1f0dd}",  "\u{1f0de}",
+    // Diamonds
+    "\u{1f0c1}", "\u{1f0c2}",  "\u{1f0c3}",  "\u{1f0c4}",  "\u{1f0c5}",
+    "\u{1f0c6}", "\u{1f0c7}",  "\u{1f0c8}",  "\u{1f0c9}",  "\u{1f0ca}",
+    "\u{1f0cb}", "\u{1f0cd}",  "\u{1f0ce}"
+];
+
+class Hand
+{
+    constructor()
+    {
+        this.clear();
+    }
+
+    clear()
+    {
+        this._cards = [];
+        this._rank = 0;
+    }
+
+    takeCard(card)
+    {
+        this._cards.push(card);
+    }
+
+    score()
+    {
+        // Sort highest rank to lowest
+        this._cards.sort((a, b) => {
+            return CardDeck.cardRank(b) - CardDeck.cardRank(a);
+        });
+
+        let handString = this._cards.join("");
+
+        let flushResult = handString.match(Hand.FlushRegExp);
+        let straightResult = handString.match(Hand.StraightRegExp);
+        let ofAKindResult = handString.match(Hand.OfAKindRegExp);
+
+        if (flushResult) {
+            if (straightResult) {
+                if (straightResult[1])
+                    this._rank = Hand.RoyalFlush;
+                else
+                    this._rank = Hand.StraightFlush
+            } else
+                this._rank = Hand.Flush;
+
+            this._rank |= CardDeck.cardRank(this._cards[0]) << 16 | CardDeck.cardRank(this._cards[1]) << 12;
+        } else if (straightResult)
+            this._rank = Hand.Straight | CardDeck.cardRank(this._cards[0]) << 16 | CardDeck.cardRank(this._cards[1]) << 12;
+        else if (ofAKindResult) {
+            // When comparing lengths, a matched unicode character has a length of 2.
+            // Therefore expected lengths are doubled, e.g a pair will have a match length of 4.
+            if (ofAKindResult[0].length == 8)
+                this._rank = Hand.FourOfAKind | CardDeck.cardRank(this._cards[0]);
+            else {
+                // Found pair or three of a kind.  Check for two pair or full house.
+                let firstOfAKind = ofAKindResult[0];
+                let remainingCardsIndex = handString.indexOf(firstOfAKind) + firstOfAKind.length;
+                let secondOfAKindResult;
+                if (remainingCardsIndex <= 6
+                    && (secondOfAKindResult = handString.slice(remainingCardsIndex).match(Hand.OfAKindRegExp))) {
+                    if ((firstOfAKind.length == 6 && secondOfAKindResult[0].length == 4)
+                        || (firstOfAKind.length == 4 && secondOfAKindResult[0].length == 6)) {
+                        let threeOfAKindCardRank;
+                        let twoOfAKindCardRank;
+                        if (firstOfAKind.length == 6) {
+                            threeOfAKindCardRank = CardDeck.cardRank(firstOfAKind.slice(0,2));
+                            twoOfAKindCardRank = CardDeck.cardRank(secondOfAKindResult[0].slice(0,2));
+                        } else {
+                            threeOfAKindCardRank = CardDeck.cardRank(secondOfAKindResult[0].slice(0,2));
+                            twoOfAKindCardRank = CardDeck.cardRank(firstOfAKind.slice(0,2));
+                        }
+                        this._rank = Hand.FullHouse | threeOfAKindCardRank << 16 | threeOfAKindCardRank < 12 | threeOfAKindCardRank << 8 | twoOfAKindCardRank << 4 | twoOfAKindCardRank;
+                    } else if (firstOfAKind.length == 4 && secondOfAKindResult[0].length == 4) {
+                        let firstPairCardRank = CardDeck.cardRank(firstOfAKind.slice(0,2));
+                        let SecondPairCardRank = CardDeck.cardRank(secondOfAKindResult[0].slice(0,2));
+                        let otherCardRank;
+                        // Due to sorting, the other card is at index 0, 4 or 8
+                        if (firstOfAKind.codePointAt(0) == handString.codePointAt(0)) {
+                            if (secondOfAKindResult[0].codePointAt(0) == handString.codePointAt(4))
+                                otherCardRank = CardDeck.cardRank(handString.slice(8,10));
+                            else
+                                otherCardRank = CardDeck.cardRank(handString.slice(4,6));
+                        } else
+                            otherCardRank = CardDeck.cardRank(handString.slice(0,2));
+
+                        this._rank = Hand.TwoPair | firstPairCardRank << 16 | firstPairCardRank << 12 | SecondPairCardRank << 8 | SecondPairCardRank << 4 | otherCardRank;
+                    }
+                } else {
+                    let ofAKindCardRank = CardDeck.cardRank(firstOfAKind.slice(0,2));
+                    let otherCardsRank = 0;
+                    for (let card of this._cards) {
+                        let cardRank = CardDeck.cardRank(card);
+                        if (cardRank != ofAKindCardRank)
+                            otherCardsRank = (otherCardsRank << 4) | cardRank;
+                    }
+
+                    if (firstOfAKind.length == 6)
+                        this._rank = Hand.ThreeOfAKind | ofAKindCardRank << 16 | ofAKindCardRank << 12 | ofAKindCardRank << 8 | otherCardsRank;
+                    else
+                        this._rank = Hand.Pair | ofAKindCardRank << 16 | ofAKindCardRank << 12 | otherCardsRank;
+                }
+            }
+        } else {
+            this._rank = 0;
+            for (let card of this._cards) {
+                let cardRank = CardDeck.cardRank(card);
+                this._rank = (this._rank << 4) | cardRank;
+            }
+        }
+    }
+
+    get rank()
+    {
+        return this._rank;
+    }
+
+    toString()
+    {
+        return this._cards.join("");
+    }
+}
+
+Hand.FlushRegExp = new RegExp("([\u{1f0a1}-\u{1f0ae}]{5})|([\u{1f0b1}-\u{1f0be}]{5})|([\u{1f0c1}-\u{1f0ce}]{5})|([\u{1f0d1}-\u{1f0de}]{5})", "u");
+
+Hand.StraightRegExp = new RegExp("([\u{1f0a1}\u{1f0b1}\u{1f0d1}\u{1f0c1}][\u{1f0ae}\u{1f0be}\u{1f0de}\u{1f0ce}][\u{1f0ad}\u{1f0bd}\u{1f0dd}\u{1f0cd}][\u{1f0ab}\u{1f0bb}\u{1f0db}\u{1f0cb}][\u{1f0aa}\u{1f0ba}\u{1f0da}\u{1f0ca}])|[\u{1f0ae}\u{1f0be}\u{1f0de}\u{1f0ce}][\u{1f0ad}\u{1f0bd}\u{1f0dd}\u{1f0cd}][\u{1f0ab}\u{1f0bb}\u{1f0db}\u{1f0cb}][\u{1f0aa}\u{1f0ba}\u{1f0da}\u{1f0ca}][\u{1f0a9}\u{1f0b9}\u{1f0d9}\u{1f0c9}]|[\u{1f0ad}\u{1f0bd}\u{1f0dd}\u{1f0cd}][\u{1f0ab}\u{1f0bb}\u{1f0db}\u{1f0cb}][\u{1f0aa}\u{1f0ba}\u{1f0da}\u{1f0ca}][\u{1f0a9}\u{1f0b9}\u{1f0d9}\u{1f0c9}][\u{1f0a8}\u{1f0b8}\u{1f0d8}\u{1f0c8}]|[\u{1f0ab}\u{1f0bb}\u{1f0db}\u{1f0cb}][\u{1f0aa}\u{1f0ba}\u{1f0da}\u{1f0ca}][\u{1f0a9}\u{1f0b9}\u{1f0d9}\u{1f0c9}][\u{1f0a8}\u{1f0b8}\u{1f0d8}\u{1f0c8}][\u{1f0a7}\u{1f0b7}\u{1f0d7}\u{1f0c7}]|[\u{1f0aa}\u{1f0ba}\u{1f0da}\u{1f0ca}][\u{1f0a9}\u{1f0b9}\u{1f0d9}\u{1f0c9}][\u{1f0a8}\u{1f0b8}\u{1f0d8}\u{1f0c8}][\u{1f0a7}\u{1f0b7}\u{1f0d7}\u{1f0c7}][\u{1f0a6}\u{1f0b6}\u{1f0d6}\u{1f0c6}]|[\u{1f0a9}\u{1f0b9}\u{1f0d9}\u{1f0c9}][\u{1f0a8}\u{1f0b8}\u{1f0d8}\u{1f0c8}][\u{1f0a7}\u{1f0b7}\u{1f0d7}\u{1f0c7}][\u{1f0a6}\u{1f0b6}\u{1f0d6}\u{1f0c6}][\u{1f0a5}\u{1f0b5}\u{1f0d5}\u{1f0c5}]|[\u{1f0a8}\u{1f0b8}\u{1f0d8}\u{1f0c8}][\u{1f0a7}\u{1f0b7}\u{1f0d7}\u{1f0c7}][\u{1f0a6}\u{1f0b6}\u{1f0d6}\u{1f0c6}][\u{1f0a5}\u{1f0b5}\u{1f0d5}\u{1f0c5}][\u{1f0a4}\u{1f0b4}\u{1f0d4}\u{1f0c4}]|[\u{1f0a7}\u{1f0b7}\u{1f0d7}\u{1f0c7}][\u{1f0a6}\u{1f0b6}\u{1f0d6}\u{1f0c6}][\u{1f0a5}\u{1f0b5}\u{1f0d5}\u{1f0c5}][\u{1f0a4}\u{1f0b4}\u{1f0d4}\u{1f0c4}][\u{1f0a3}\u{1f0b3}\u{1f0d3}\u{1f0c3}]|[\u{1f0a6}\u{1f0b6}\u{1f0d6}\u{1f0c6}][\u{1f0a5}\u{1f0b5}\u{1f0d5}\u{1f0c5}][\u{1f0a4}\u{1f0b4}\u{1f0d4}\u{1f0c4}][\u{1f0a3}\u{1f0b3}\u{1f0d3}\u{1f0c3}][\u{1f0a2}\u{1f0b2}\u{1f0d2}\u{1f0c2}]|[\u{1f0a1}\u{1f0b1}\u{1f0d1}\u{1f0c1}][\u{1f0a5}\u{1f0b5}\u{1f0d5}\u{1f0c5}][\u{1f0a4}\u{1f0b4}\u{1f0d4}\u{1f0c4}][\u{1f0a3}\u{1f0b3}\u{1f0d3}\u{1f0c3}][\u{1f0a2}\u{1f0b2}\u{1f0d2}\u{1f0c2}]", "u");
+
+Hand.OfAKindRegExp = new RegExp("(?:[\u{1f0a1}\u{1f0b1}\u{1f0d1}\u{1f0c1}]{2,4})|(?:[\u{1f0ae}\u{1f0be}\u{1f0de}\u{1f0ce}]{2,4})|(?:[\u{1f0ad}\u{1f0bd}\u{1f0dd}\u{1f0cd}]{2,4})|(?:[\u{1f0ab}\u{1f0bb}\u{1f0db}\u{1f0cb}]{2,4})|(?:[\u{1f0aa}\u{1f0ba}\u{1f0da}\u{1f0ca}]{2,4})|(?:[\u{1f0a9}\u{1f0b9}\u{1f0d9}\u{1f0c9}]{2,4})|(?:[\u{1f0a8}\u{1f0b8}\u{1f0d8}\u{1f0c8}]{2,4})|(?:[\u{1f0a7}\u{1f0b7}\u{1f0d7}\u{1f0c7}]{2,4})|(?:[\u{1f0a6}\u{1f0b6}\u{1f0d6}\u{1f0c6}]{2,4})|(?:[\u{1f0a5}\u{1f0b5}\u{1f0d5}\u{1f0c5}]{2,4})|(?:[\u{1f0a4}\u{1f0b4}\u{1f0d4}\u{1f0c4}]{2,4})|(?:[\u{1f0a3}\u{1f0b3}\u{1f0d3}\u{1f0c3}]{2,4})|(?:[\u{1f0a2}\u{1f0b2}\u{1f0d2}\u{1f0c2}]{2,4})", "u");
+
+Hand.RoyalFlush = 0x900000;
+Hand.StraightFlush = 0x800000;
+Hand.FourOfAKind = 0x700000;
+Hand.FullHouse = 0x600000;
+Hand.Flush = 0x500000;
+Hand.Straight = 0x400000;
+Hand.ThreeOfAKind = 0x300000;
+Hand.TwoPair = 0x200000;
+Hand.Pair = 0x100000;
+
+class Player extends Hand
+{
+    constructor(name)
+    {
+        super();
+        this._name = name;
+        this._wins = 0;
+        this._handTypeCounts = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+    }
+
+    scoreHand()
+    {
+        this.score();
+        let handType = this.rank >> 20;
+        this._handTypeCounts[handType]++;
+    }
+
+    wonHand()
+    {
+        this._wins++
+    }
+
+    get name()
+    {
+        return this._name;
+    }
+
+    get hand()
+    {
+        return super.toString();
+    }
+
+    get wins()
+    {
+        return this._wins;
+    }
+
+    get handTypeCounts()
+    {
+        return this._handTypeCounts;
+    }
+}
+
+function playHands(players)
+{
+    let cardDeck = new CardDeck();
+    let handsPlayed = 0;
+    let highestRank = 0;
+
+    do {
+        cardDeck.shuffle();
+
+        for (let player of players)
+            player.clear();
+
+        for (let i = 0; i < 5; i++) {
+            for (let player of players)
+                player.takeCard(cardDeck.dealOneCard());
+        }
+
+        for (let player of players)
+            player.scoreHand();
+
+        handsPlayed++;
+
+        highestRank = 0;
+
+        for (let player of players) {
+            if (player.rank > highestRank)
+                highestRank = player.rank;
+        }
+
+        for (let player of players) {
+            // We count ties as wins for each player.
+            if (player.rank == highestRank)
+                player.wonHand();
+        }
+    } while (handsPlayed < 2000);
+}
index b740543..d0134a6 100644 (file)
@@ -24,6 +24,8 @@
 
         <p>Offline Assembler is the lexer, parser and AST layer of the offline assembler for JavaScriptCore.  It has been ported to JavaScript from the original Ruby.</p>
 
+        <p>UniPoker is a 5 card stud poker simulation using the Unicode playing cards code points, U+1F0A1..U+1F0DE, as the card representation in the code.  Scoring of hands is done with three regular expressions, one to check for a flush, one to check for straights, and one to check for pairs, three of a kind and four of a kind.  The last regular expression is used twice as needed to find hands with 2 pairs or a full house.
+
         <p>Flight Planner is a newly written benchmark taken from a flight management web application.  Flight Planner parses aircraft flight plans and computes distance, course and elapsed times for legs of flight plans as well as total time.  It uses FAA data for airports, navigation aids and airways.  The flight management app was originally written to help compete in a flying proficiency event.  See <a href="http://www.hwdairrally.org/index.shtml">Hayward Air Rally</a>.
         </p>
 
index 101776b..bf22d8d 100644 (file)
@@ -53,6 +53,7 @@ load("sunspider_benchmark.js");
 load("octane2_benchmark.js");
 load("basic_benchmark.js");
 load("offline_assembler_benchmark.js");
+load("unipoker_benchmark.js");
 load("flightplan_benchmark.js");
 load("flightplan_unicode_benchmark.js");
 load("glue.js");
index e6eb38d..a62da3a 100644 (file)
@@ -39,7 +39,7 @@ function reportResult(...args) {
 }
 
 {
-    const title = "RexBench 0.92";
+    const title = "RexBench 0.93";
     if (isInBrowser) {
         document.title = title;
     } else {
@@ -51,6 +51,7 @@ driver.addBenchmark(RegexDNABenchmarkRunner);
 driver.addBenchmark(Octane2RegExpBenchmarkRunner);
 driver.addBenchmark(BasicBenchmarkRunner);
 driver.addBenchmark(OfflineAssemblerBenchmarkRunner);
+driver.addBenchmark(UniPokerBenchmarkRunner);
 driver.addBenchmark(FlightPlannerBenchmarkRunner);
 driver.addBenchmark(FlightPlannerUnicodeBenchmarkRunner);
 driver.readyTrigger();
index 9013d9e..35ab23f 100644 (file)
             </div>
         </div>
 
+        <div class="unipoker test">
+            <h2 id="UniPokerMessage">UniPoker</h2> 
+            
+            <div class="score">
+                <label>First Iteration</label>
+                <span id="UniPokerFirstIteration">
+                    <span class="value">0</span><span class="units">ms</span>                    
+                </span>
+            </div>
+            <div class="score">
+                <label>Worst 4 Iteratons</label>
+                <span id="UniPokerAverageWorstCase">
+                    <span class="value">0</span><span class="units">ms</span>                    
+                </span>
+            </div>
+
+            <div class="score">
+                <label>Average</label>
+                <span id="UniPokerSteadyState">
+                    <span class="value">0</span><span class="units">ms</span>                    
+                </span>
+            </div>
+        </div>
+
         <div class="flight planner test">
             <h2 id="FlightPlannerMessage">Flight Planner</h2> 
             
     <script src="octane2_benchmark.js"></script>
     <script src="basic_benchmark.js"></script>
     <script src="offline_assembler_benchmark.js"></script>
+    <script src="unipoker_benchmark.js"></script>
     <script src="flightplan_benchmark.js"></script>
     <script src="flightplan_unicode_benchmark.js"></script>
     <script src="glue.js"></script>
diff --git a/PerformanceTests/RexBench/unipoker_benchmark.js b/PerformanceTests/RexBench/unipoker_benchmark.js
new file mode 100644 (file)
index 0000000..ecb2010
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+"use strict";
+
+const UniPokerBenchmarkCode = String.raw`
+<script src="benchmark.js"></script>
+<script src="UniPoker/poker.js"></script>
+<script src="UniPoker/expected.js"></script>
+<script src="UniPoker/benchmark.js"></script>
+<script>
+"use strict";
+var results = [];
+var benchmark = new UniPokerBenchmark();
+var numIterations = 20;
+benchmark.runIterations(numIterations, results);
+reportResult(results);
+</script>`;
+
+
+let runUniPokerBenchmark = null;
+if (!isInBrowser) {
+    let sources = [
+        "benchmark.js"
+        , "UniPoker/poker.js"
+        , "UniPoker/expected.js"
+        , "UniPoker/benchmark.js"
+    ];
+
+    runUniPokerBenchmark = makeBenchmarkRunner(sources, "UniPokerBenchmark", 20);
+}
+
+const UniPokerBenchmarkRunner = {
+    name: "UniPoker",
+    code: UniPokerBenchmarkCode,
+    run: runUniPokerBenchmark,
+    cells: {}
+};
+
+if (isInBrowser) {
+    UniPokerBenchmarkRunner.cells = {
+        firstIteration: document.getElementById("UniPokerFirstIteration"),
+        averageWorstCase: document.getElementById("UniPokerAverageWorstCase"),
+        steadyState: document.getElementById("UniPokerSteadyState"),
+        message: document.getElementById("UniPokerMessage")
+    };
+}