Skip to content
Tobias Tenebra edited this page Apr 13, 2015 · 15 revisions

Analyzes a sequence of 2 private cards and 3 community cards for the highest poker hand combination. Usually instantiated by the PokerHandAnalyzer class.

The PokerHand class analyzes permutations of supplied cards and creates a numerically ranked list of the best hands found. This is accomplished by computing values based on two pieces of supplied XML configuration data: the <cards> node and the <hands> node.

Grouping, Sorting, and Matching

In CypherPoker a "matched" card or hand is one that matches a pattern defined in the game's XML definition, in the <hands> node. Here's an example that defines a high card (no match) pattern:

<hand name="High Card" rank="1" points="1" aces="high" groupby="facevalue" match="*" />

The valid attributes of a <hand> node are as follows:

name: The human-readable display name of the hand definition.

rank: The numeric rank of the hand. Ranks start at 1 and must increase uniquely by 1 for each new definition (the next highest rank must be 2, the next one 3, etc.) This allows nodes to be stored in non-linear ways within the XML data.

points: The number of points awarded when this definition is matched (see "Scoring" section below).

aces: Specifies how ace values are calculated for this definition. A "high" value causes the second facevalue value defined for any matching aces (for example facevalue="1;14") to be included in the points calculation, otherwise the "low" value is used instead.

groupby: Defines how cards are initially grouped (see "Grouping" below).

sort: An optional node definining a secondary sorting operation (see "Sorting" below).

match: Defines the final match pattern for the definition (see "Matching" below).

Each <hand> definition is analyzed in order from highest to lowest rank on the cards provided to a PokerHand instance. As soon as a match is established the total points for the hand are calculated and the highest hand is considered found. It is technically possible for cards to match no provided definition but usually the last node includes a catch-all match (match="*").

Grouping

The first operation applied to the supplied cards to prepare them for a match is grouping. Valid values for the groupby attribute include any attribute name defined for any card definition. For example, any of the following are valid:

groupby="facevalue"

groupby="color"

groupby="facetext"

Additional attributes may be found in any card definition:

<card name="Three of Clubs" facevalue="3" facetext="three" color="black" suit="clubs" class="ThreeOfClubs" />

Based on this data the supplied cards are grouped as specified. For example, if groupby="facetext" is defined for a hand any supplied cards that have the same "facetext" attribute (name and value) are grouped together.

To illustrate this, consider the following combination supplied to PokerHand:

Ace of Spades Two of Hearts Two of Diamonds Queen of Hearts King of Hearts

In this case using groupby="facetext" will generate 4 groups, stored as arrays, based on the cards' definitions:

[Ace of Spades] [Two of Hearts, Two of Diamonds] [Queen of Hearts] [King of Hearts]

Using groupby="color" will produce only 2 groups:

[Ace of Spades] [Two of Hearts, Two of Diamonds, Queen of Hearts, King of Hearts]

A wilcard grouping operation (groupby="*") always produces only one group.

After sorting, groups are used in the Matching (see below) operation.

Sorting

Sorting is an optional step performed with some <hand> definitions that arranges cards within their groups by their numeric facevalue attribue.

A sort may be either "asc" (ascending) or "des" (descending). If not defined "asc" is assumed.

Matching

In this final step the groups created in the Grouping step are compared against patterns defined in the match attribute. A single wildcard (match="*") matches all cards in all groupings.

A match definition is made up of name-value pairs separated by semicolons (;). The following is an example for a single permutation of two pairs:

match="facetext:*,*;facetext:*,*;facetext:*"

This definition contains three groups:

facetext:*,* facetext:*,* facetext:*

To match the definition there must exist one group of cards two cards (,) containing any (*) facetext values, another group of two cards with any facetext values, and a final group of one card with any facetext value.

Any values are accepted with wildcards so a group match like color:*,* would also work in the above example.

Groups aren't sorted so it's possible that the above definition for two pairs may not match. For this reason additional permutations are included to catch any combination of cards:

facetext:*,*;facetext:*;facetext:*,*" facetext:*;facetext:*,*;facetext:*,*"

Some <hand> definitions include specific values for matching, such as the following for a straight flush:

facetext:ace,two,three,four,five

In this definition grouping is by "suit" (groupby="suit") which should produce only one group. Ascending sorting is applied (sort="asc") to produce a grouped list of cards which should match the facetext pattern "ace,two,three,four,five".

The highest-ranking definition is always considered the best match whenever more than one definition matches the supplied cards.

Scoring

After sorting and grouping cards the numeric rank of a hand is calculated as follows:

(matched hand points * _handPointsMultiplier) + (matched card facevalue * _matchMultiplier) + card facevalue ...

The first and largest portion calculates the points of the hand found by multiplying the "points" attribute value of the associated node. For example, if the player has a full house the "matched hand points" value would be 7 based on the associated data:

<hand name="Full House" rank="23" points="7" aces="high" groupby="facevalue" match="facetext:*,*,*;facetext:*,*" />

This points value is multiplied by the _handPointsMultiplier value, a class constant currently set to 1000000. In the above example the first portion of the calculation will produce a value of 7000000.

The second part of the calculation is repeated for every card that matches the hand pattern. In the case of a full house this would include all 5 cards but other combinations may have fewer cards. For example, a pair would only calculate this value for 2 cards. Each matched card is multiplied by the _matchMultiplier class constant, currently set to 1000.

In the example of a full house using three aces and two queens would we use the facevalue value from the associated data nodes:

<card name="Ace of Spades" facevalue="1;14" facetext="ace" color="black" suit="spades" class="AceOfSpades" />

<card name="Ace of Hearts" facevalue="1;14" facetext="ace" color="red" suit="hearts" class="AceOfHearts" />

<card name="Ace of Diamonds" facevalue="1;14" facetext="ace" color="red" suit="diamonds" class="AceOfDiamonds" />

<card name="Queen of Diamonds" facevalue="12" facetext="queen" color="red" suit="diamonds" class="QueenOfDiamonds" />

<card name="Queen of Clubs" facevalue="12" facetext="queen" color="black" suit="clubs" class="QueenOfClubs" />

Because the hand definition defines aces as having a high value (aces="high") we use the second facevalue number defined for aces in our calculation. The above example would therefore be calculated as:

(14 * 1000) + (14 *1000) + (14 * 1000) + (12 * 1000) + (12 * 1000) = 66000

The final part of the calculation simply adds the facevalue values of any non-matched cards supplied to the PokerHand instance. In the above example there are no un-matched cards since all 5 cards make up the full house. In the case of a pair, however, the 3 non-matched cards would simply be added to the overall total without including any multipliers. Any non-matching aces are considered low.

The final numeric hand rank is the sum of all three calculations above. In the full house example the final hand rank would be:

7000000 + 66000 = 7066000

Source code and inline documentation: https://github.com/monicanagent/cypherpoker/blob/master/GameEngines/PokerCardGame/src/PokerHand.as

Clone this wiki locally