Make JetStream 2
[WebKit-https.git] / PerformanceTests / JetStream2 / RexBench / UniPoker / poker.js
1 /*
2  * Copyright (C) 2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25 "use strict";
26
27
28 class CardDeck
29 {
30     constructor()
31     {
32         this.newDeck();
33     }
34
35     newDeck()
36     {
37         // Make a shallow copy of a new deck
38         this._cards = CardDeck._newDeck.slice(0);
39     }
40
41     shuffle()
42     {
43         this.newDeck();
44
45         for (let index = 52; index !== 0;) {
46             // Select a random card
47             let randomIndex = Math.floor(Math.random() * index);
48             index--;
49
50             // Swap the current card with the random card
51             let tempCard = this._cards[index];
52             this._cards[index] = this._cards[randomIndex];
53             this._cards[randomIndex] = tempCard;
54         }
55     }
56
57     dealOneCard()
58     {
59         return this._cards.shift();
60     }
61
62     static cardRank(card)
63     {
64         // This returns a numeric value for a card.
65         // Ace is highest.
66
67         let rankOfCard = card.codePointAt(0) & 0xf;
68         if (rankOfCard == 0x1) // Make Aces higher than Kings
69             rankOfCard = 0xf;
70
71         return rankOfCard;
72     }
73
74     static cardName(card)
75     {      
76         if (typeof(card) == "string")
77             card = card.codePointAt(0);
78         return this._rankNames[card & 0xf];
79     }
80 }
81
82 CardDeck._rankNames = [
83     "", "Ace", "2", "3", "4", "5", "6", "7", "8", "9", "10", "Jack", "", "Queen", "King"
84 ];
85
86 CardDeck._newDeck = [
87     // Spades
88     "\u{1f0a1}", "\u{1f0a2}",  "\u{1f0a3}",  "\u{1f0a4}",  "\u{1f0a5}",
89     "\u{1f0a6}", "\u{1f0a7}",  "\u{1f0a8}",  "\u{1f0a9}",  "\u{1f0aa}",
90     "\u{1f0ab}", "\u{1f0ad}",  "\u{1f0ae}",
91     // Hearts
92     "\u{1f0b1}", "\u{1f0b2}",  "\u{1f0b3}",  "\u{1f0b4}",  "\u{1f0b5}",
93     "\u{1f0b6}", "\u{1f0b7}",  "\u{1f0b8}",  "\u{1f0b9}",  "\u{1f0ba}",
94     "\u{1f0bb}", "\u{1f0bd}",  "\u{1f0be}",
95     // Clubs
96     "\u{1f0d1}", "\u{1f0d2}",  "\u{1f0d3}",  "\u{1f0d4}",  "\u{1f0d5}",
97     "\u{1f0d6}", "\u{1f0d7}",  "\u{1f0d8}",  "\u{1f0d9}",  "\u{1f0da}",
98     "\u{1f0db}", "\u{1f0dd}",  "\u{1f0de}",
99     // Diamonds
100     "\u{1f0c1}", "\u{1f0c2}",  "\u{1f0c3}",  "\u{1f0c4}",  "\u{1f0c5}",
101     "\u{1f0c6}", "\u{1f0c7}",  "\u{1f0c8}",  "\u{1f0c9}",  "\u{1f0ca}",
102     "\u{1f0cb}", "\u{1f0cd}",  "\u{1f0ce}"
103 ];
104
105 class Hand
106 {
107     constructor()
108     {
109         this.clear();
110     }
111
112     clear()
113     {
114         this._cards = [];
115         this._rank = 0;
116     }
117
118     takeCard(card)
119     {
120         this._cards.push(card);
121     }
122
123     score()
124     {
125         // Sort highest rank to lowest
126         this._cards.sort((a, b) => {
127             return CardDeck.cardRank(b) - CardDeck.cardRank(a);
128         });
129
130         let handString = this._cards.join("");
131
132         let flushResult = handString.match(Hand.FlushRegExp);
133         let straightResult = handString.match(Hand.StraightRegExp);
134         let ofAKindResult = handString.match(Hand.OfAKindRegExp);
135
136         if (flushResult) {
137             if (straightResult) {
138                 if (straightResult[1])
139                     this._rank = Hand.RoyalFlush;
140                 else
141                     this._rank = Hand.StraightFlush
142             } else
143                 this._rank = Hand.Flush;
144
145             this._rank |= CardDeck.cardRank(this._cards[0]) << 16 | CardDeck.cardRank(this._cards[1]) << 12;
146         } else if (straightResult)
147             this._rank = Hand.Straight | CardDeck.cardRank(this._cards[0]) << 16 | CardDeck.cardRank(this._cards[1]) << 12;
148         else if (ofAKindResult) {
149             // When comparing lengths, a matched unicode character has a length of 2.
150             // Therefore expected lengths are doubled, e.g a pair will have a match length of 4.
151             if (ofAKindResult[0].length == 8)
152                 this._rank = Hand.FourOfAKind | CardDeck.cardRank(this._cards[0]);
153             else {
154                 // Found pair or three of a kind.  Check for two pair or full house.
155                 let firstOfAKind = ofAKindResult[0];
156                 let remainingCardsIndex = handString.indexOf(firstOfAKind) + firstOfAKind.length;
157                 let secondOfAKindResult;
158                 if (remainingCardsIndex <= 6
159                     && (secondOfAKindResult = handString.slice(remainingCardsIndex).match(Hand.OfAKindRegExp))) {
160                     if ((firstOfAKind.length == 6 && secondOfAKindResult[0].length == 4)
161                         || (firstOfAKind.length == 4 && secondOfAKindResult[0].length == 6)) {
162                         let threeOfAKindCardRank;
163                         let twoOfAKindCardRank;
164                         if (firstOfAKind.length == 6) {
165                             threeOfAKindCardRank = CardDeck.cardRank(firstOfAKind.slice(0,2));
166                             twoOfAKindCardRank = CardDeck.cardRank(secondOfAKindResult[0].slice(0,2));
167                         } else {
168                             threeOfAKindCardRank = CardDeck.cardRank(secondOfAKindResult[0].slice(0,2));
169                             twoOfAKindCardRank = CardDeck.cardRank(firstOfAKind.slice(0,2));
170                         }
171                         this._rank = Hand.FullHouse | threeOfAKindCardRank << 16 | threeOfAKindCardRank < 12 | threeOfAKindCardRank << 8 | twoOfAKindCardRank << 4 | twoOfAKindCardRank;
172                     } else if (firstOfAKind.length == 4 && secondOfAKindResult[0].length == 4) {
173                         let firstPairCardRank = CardDeck.cardRank(firstOfAKind.slice(0,2));
174                         let SecondPairCardRank = CardDeck.cardRank(secondOfAKindResult[0].slice(0,2));
175                         let otherCardRank;
176                         // Due to sorting, the other card is at index 0, 4 or 8
177                         if (firstOfAKind.codePointAt(0) == handString.codePointAt(0)) {
178                             if (secondOfAKindResult[0].codePointAt(0) == handString.codePointAt(4))
179                                 otherCardRank = CardDeck.cardRank(handString.slice(8,10));
180                             else
181                                 otherCardRank = CardDeck.cardRank(handString.slice(4,6));
182                         } else
183                             otherCardRank = CardDeck.cardRank(handString.slice(0,2));
184
185                         this._rank = Hand.TwoPair | firstPairCardRank << 16 | firstPairCardRank << 12 | SecondPairCardRank << 8 | SecondPairCardRank << 4 | otherCardRank;
186                     }
187                 } else {
188                     let ofAKindCardRank = CardDeck.cardRank(firstOfAKind.slice(0,2));
189                     let otherCardsRank = 0;
190                     for (let card of this._cards) {
191                         let cardRank = CardDeck.cardRank(card);
192                         if (cardRank != ofAKindCardRank)
193                             otherCardsRank = (otherCardsRank << 4) | cardRank;
194                     }
195
196                     if (firstOfAKind.length == 6)
197                         this._rank = Hand.ThreeOfAKind | ofAKindCardRank << 16 | ofAKindCardRank << 12 | ofAKindCardRank << 8 | otherCardsRank;
198                     else
199                         this._rank = Hand.Pair | ofAKindCardRank << 16 | ofAKindCardRank << 12 | otherCardsRank;
200                 }
201             }
202         } else {
203             this._rank = 0;
204             for (let card of this._cards) {
205                 let cardRank = CardDeck.cardRank(card);
206                 this._rank = (this._rank << 4) | cardRank;
207             }
208         }
209     }
210
211     get rank()
212     {
213         return this._rank;
214     }
215
216     toString()
217     {
218         return this._cards.join("");
219     }
220 }
221
222 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");
223
224 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");
225
226 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");
227
228 Hand.RoyalFlush = 0x900000;
229 Hand.StraightFlush = 0x800000;
230 Hand.FourOfAKind = 0x700000;
231 Hand.FullHouse = 0x600000;
232 Hand.Flush = 0x500000;
233 Hand.Straight = 0x400000;
234 Hand.ThreeOfAKind = 0x300000;
235 Hand.TwoPair = 0x200000;
236 Hand.Pair = 0x100000;
237
238 class Player extends Hand
239 {
240     constructor(name)
241     {
242         super();
243         this._name = name;
244         this._wins = 0;
245         this._handTypeCounts = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
246     }
247
248     scoreHand()
249     {
250         this.score();
251         let handType = this.rank >> 20;
252         this._handTypeCounts[handType]++;
253     }
254
255     wonHand()
256     {
257         this._wins++
258     }
259
260     get name()
261     {
262         return this._name;
263     }
264
265     get hand()
266     {
267         return super.toString();
268     }
269
270     get wins()
271     {
272         return this._wins;
273     }
274
275     get handTypeCounts()
276     {
277         return this._handTypeCounts;
278     }
279 }
280
281 function playHands(players)
282 {
283     let cardDeck = new CardDeck();
284     let handsPlayed = 0;
285     let highestRank = 0;
286
287     do {
288         cardDeck.shuffle();
289
290         for (let player of players)
291             player.clear();
292
293         for (let i = 0; i < 5; i++) {
294             for (let player of players)
295                 player.takeCard(cardDeck.dealOneCard());
296         }
297
298         for (let player of players)
299             player.scoreHand();
300
301         handsPlayed++;
302
303         highestRank = 0;
304
305         for (let player of players) {
306             if (player.rank > highestRank)
307                 highestRank = player.rank;
308         }
309
310         for (let player of players) {
311             // We count ties as wins for each player.
312             if (player.rank == highestRank)
313                 player.wonHand();
314         }
315     } while (handsPlayed < 2000);
316 }