#!/usr/bin/env python3 import numpy as np from typing import List, Dict, Tuple import itertools from poker.card import Card, ShortDeckRank, Suit from shortdeck.hand_evaluator import HandEvaluator class ShortDeckHistGenerator: def __init__(self): # (36张牌:6-A) self.full_deck = [] for rank in ShortDeckRank: for suit in Suit: self.full_deck.append(Card(rank, suit)) self.hand_evaluator = HandEvaluator() print(f"初始化短牌型EHS直方图生成器,牌组大小: {len(self.full_deck)}") print(f"牌型范围: {ShortDeckRank.SIX.name}-{ShortDeckRank.ACE.name}") def generate_river_ehs(self, player_cards, board_cards) -> float: """ River阶段胜率计算(确定性结果) 5张公共牌已知,直接计算对所有可能对手的胜率 player_cards: 玩家的2张底牌 board_cards: 5张公共牌 Returns: 胜率值 (0-1之间) """ if len(player_cards) != 2: raise ValueError("玩家必须有2张底牌") if len(board_cards) != 5: raise ValueError("River阶段必须有5张公共牌") # 计算玩家的7张牌牌力 player_7_cards = player_cards + board_cards player_strength = self.hand_evaluator.evaluate_hand(player_7_cards) # 获取剩余的牌 used_cards = player_cards + board_cards remaining_cards = [card for card in self.full_deck if card not in used_cards] # 计算对所有可能对手的胜率 wins = 0 total_player2s = 0 # 遍历所有可能的对手底牌组合 for player2_cards in itertools.combinations(remaining_cards, 2): player2_7_cards = list(player2_cards) + board_cards player2_strength = self.hand_evaluator.evaluate_hand(player2_7_cards) if player_strength > player2_strength: wins += 2 elif player_strength == player2_strength: wins += 1 total_player2s += 2 ehs = wins / total_player2s return ehs def generate_turn_histogram(self, player_cards, board_cards, num_bins: int = 30) -> np.ndarray: """ player_cards: 玩家的2张底牌 board_cards: 4张公共牌 num_bins: 30个bin(短牌型应该是30) """ if len(player_cards) != 2: raise ValueError("玩家必须有2张底牌") if len(board_cards) != 4: raise ValueError("Turn阶段必须有4张公共牌") # 获取剩余的牌(可作为River牌) used_cards = player_cards + board_cards remaining_cards = [card for card in self.full_deck if card not in used_cards] ehs_values = [] histogram = [] # 对每张可能的River牌计算EHS for river_card in remaining_cards: full_board = board_cards + [river_card] ehs = self.generate_river_ehs(player_cards, full_board) # ehs_values.append(ehs) histogram.append(ehs) if (len(histogram) != num_bins): print(f" Turn计算: generate nums{len(histogram)} ,num_bins={num_bins}") raise # 生成直方图 # histogram, _ = np.histogram(ehs_values, bins=num_bins, range=(0, 1)) # 归一化 # histogram = histogram.astype(float) # if histogram.sum() > 0: # histogram /= histogram.sum() return histogram def generate_flop_histogram(self, player_cards, board_cards, num_bins: int = 465) -> np.ndarray: """ Flop阶段EHS直方图 3张公共牌已知,枚举所有可能的Turn+River牌组合计算EHS分布 player_cards: 玩家的2张牌 board_cards: 3张公共牌 num_bins: 直方图bin数量(短牌型应该是C(36-5,2) = 465) """ if len(player_cards) != 2: raise ValueError("玩家必须有2张牌") if len(board_cards) != 3: raise ValueError("Flop阶段必须有3张公共牌") # 获取剩余的牌 used_cards = player_cards + board_cards remaining_cards = [card for card in self.full_deck if card not in used_cards] ehs_values = [] histogram = [] # 枚举所有可能的Turn+River组合:C(31,2) = 465 for turn_river_combo in itertools.combinations(remaining_cards, 2): turn_card, river_card = turn_river_combo full_board = board_cards + [turn_card, river_card] ehs = self.generate_river_ehs(player_cards, full_board) # ehs_values.append(ehs) histogram.append(ehs) # # 验证组合数 # expected_combinations = len(list(itertools.combinations(remaining_cards, 2))) # print(f" Flop计算: C({len(remaining_cards)},2) = {expected_combinations} 种组合") # 生成直方图 # histogram, _ = np.histogram(ehs_values, bins=num_bins, range=(0, 1)) # 归一化 # histogram = histogram.astype(float) # if histogram.sum() > 0: # histogram /= histogram.sum() if (len(histogram) != num_bins): print(f" Turn计算: generate nums{len(histogram)} ,num_bins={num_bins}") raise return histogram