150 lines
5.4 KiB
Python
150 lines
5.4 KiB
Python
#!/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
|
||
|