Source code for texasholdem.evaluator.evaluator

"""
Evaluates hand strengths with optimizations in terms of speed and memory usage.

"""

import itertools
from typing import List

from texasholdem.card import card
from texasholdem.card.card import Card
from texasholdem.evaluator.lookup_table import LOOKUP_TABLE


def _five(cards: List[Card]) -> int:
    """
    Performs an evaluation given card in integer form, mapping them to
    a rank in the range [1, 7462], with lower ranks being more powerful.

    Variant of Cactus Kev's 5 card evaluator.

    Args:
        cards (List[Card]): A list of 5 card ints.
    Returns:
        int: The rank of the hand.

    """
    # if flush
    if cards[0] & cards[1] & cards[2] & cards[3] & cards[4] & 0xF000:
        hand_or = (cards[0] | cards[1] | cards[2] | cards[3] | cards[4]) >> 16
        prime = card.prime_product_from_rankbits(hand_or)
        return LOOKUP_TABLE.flush_lookup[prime]

    # otherwise
    prime = card.prime_product_from_hand(cards)
    return LOOKUP_TABLE.unsuited_lookup[prime]


[docs] def evaluate(cards: List[Card], board: List[Card]) -> int: """ Evaluates the best five-card hand from the given cards and board. Returns the corresponding rank. Args: cards (List[int]): A list of length two of card ints that a player holds. board (List[int]): A list of length 3, 4, or 5 of card ints. Returns: int: A number between 1 (highest) and 7462 (lowest) representing the relative hand rank of the given card. """ all_cards = cards + board return min(_five(hand) for hand in itertools.combinations(all_cards, 5))
[docs] def get_rank_class(hand_rank: int) -> int: """ Returns the class of hand given the hand hand_rank returned from evaluate from 9 rank classes. Example: straight flush is class 1, high card is class 9, full house is class 3. Returns: int: A rank class int describing the general category of hand from 9 rank classes. Example, straight flush is class 1, high card is class 9, full house is class 3. """ max_rank = min(rank for rank in LOOKUP_TABLE.MAX_TO_RANK_CLASS if hand_rank <= rank) return LOOKUP_TABLE.MAX_TO_RANK_CLASS[max_rank]
[docs] def rank_to_string(hand_rank: int) -> str: """ Returns a string describing the hand of the hand_rank. Example: 166 -> "Four of a Kind" Args: hand_rank (int): The rank of the hand given by :meth:`evaluate` Returns: string: A human-readable string of the hand rank (i.e. Flush, Ace High). """ return LOOKUP_TABLE.RANK_CLASS_TO_STRING[get_rank_class(hand_rank)]
[docs] def get_five_card_rank_percentage(hand_rank: int) -> float: """ The percentage of how many of the 7462 hand strengths are worse than the given one. Args: hand_rank (int): The rank of the hand given by :meth:`evaluate` Returns: float: The percentile strength of the given hand_rank (i.e. what percent of hands is worse than the given one). """ return 1 - float(hand_rank) / float(LOOKUP_TABLE.MAX_HIGH_CARD)