<think>我们正在实现麻将AI决策流程中的花色权重分析功能。根据之前的讨论,花色权重分析模块需要考虑四川麻将的缺一门规则,并动态计算各花色的权重。权重公式如下:
$$ W_f = \begin{cases}
0 & \text{若 } f \text{ 为缺失花色} \\
\alpha \cdot \frac{N_f}{N_{\text{total}}} + \beta \cdot P_{\text{win}}^f & \text{否则}
\end{cases} $$
其中:
- $f$ 表示花色(万、筒、条)
- $N_f$ 是当前手牌中花色$f$的牌数
- $N_{\text{total}}$ 是当前手牌总牌数(通常为13张,但可能因阶段而异)
- $P_{\text{win}}^f$ 是保留该花色牌时的胡牌概率(需要估算)
- $\alpha$ 和 $\beta$ 是攻防平衡系数(默认值分别为0.6和0.4)
### 实现步骤
1. **确定缺失花色**:根据四川麻将规则,玩家必须缺一门花色(即不能有三种花色)。通常,在游戏开始时会确定缺失花色,但AI也可以根据当前手牌和已出牌情况动态调整策略。这里我们假设缺失花色已经确定(作为输入)。
2. **计算各花色牌的数量**:遍历手牌,统计每个花色的牌数。
3. **估算胡牌概率**:这部分比较复杂,我们可以先使用一个简化版本来估算。简化思路:
- 对于每个非缺失花色,计算该花色形成有效组合(刻子、顺子)的潜力。
- 根据该花色已有的牌和剩余牌的数量,估算其完成面子的概率。
- 注意:完整的胡牌概率计算需要递归或动态规划,这里我们先使用一个启发式方法:用该花色当前牌数占该花色总牌数的比例来近似,或者使用预定义的表格。
4. **计算权重**:对于非缺失花色,按照公式计算权重。
### 代码实现
我们将编写一个函数`analyze_colors(hand, missing_suit, alpha=0.6, beta=0.4)`,其中:
- `hand`:当前手牌,是一个列表,每个元素是一个字符串,如"3万"、"5筒"、"7条"。
- `missing_suit`:缺失的花色,用字符串表示,如"万"、"筒"、"条"。
- `alpha`和`beta`:攻防平衡系数。
返回一个字典,包含三个花色的权重(缺失花色的权重为0)。
### 简化胡牌概率估算
由于精确计算胡牌概率非常复杂,我们这里采用一个简化方法:
- 对于每个花色,我们计算该花色在整副牌中的剩余牌数(假设初始为36张,因为每种花色有9种牌,每种4张,共36张),然后减去已经出现的牌(包括手牌和已经打出的牌)。但是,由于我们目前只有手牌信息,为了简化,我们只考虑手牌中该花色的数量,并假设剩余牌是均匀分布的。
- 我们使用一个经验公式:$P_{\text{win}}^f \approx \frac{N_f}{4}$(因为每张牌有4张,所以当$N_f$达到4时,该花色的牌足够多,胡牌概率就高)。但这样显然不够准确,我们可以改进为:$P_{\text{win}}^f \approx \min(1, \frac{N_f}{4} \times 0.5 + \frac{N_f}{9} \times 0.5)$,这个公式结合了牌的数量和该花色的牌型潜力(9种牌)。但请注意,这只是一个非常粗略的估计。
在实际应用中,我们可以使用预先训练的概率表或者更复杂的模型。但为了本功能的实现,我们采用以下简化:
$$ P_{\text{win}}^f = \min\left(1, \frac{N_f}{12}\right) $$
因为一般来说,一个花色有12张牌(包括手牌和后续摸到的)就很容易形成组合。当然,这个简化并不科学,但作为示例,我们先这样实现。
### 代码
```python
def analyze_colors(hand, missing_suit, alpha=0.6, beta=0.4):
"""
分析手牌中各花色的权重
:param hand: 当前手牌,例如: ['1万', '2万', '3万', '4筒', '5筒', '6条', '7条']
:param missing_suit: 缺失的花色,例如: '万'
:param alpha: 攻防平衡系数,用于数量比例
:param beta: 攻防平衡系数,用于胡牌概率
:return: 字典,包含三个花色的权重,例如: {'万': 0, '筒': 0.65, '条': 0.78}
"""
# 初始化三个花色
suits = ['万', '筒', '条']
# 统计每个花色的牌数
suit_count = {suit: 0 for suit in suits}
for tile in hand:
# 每张牌的最后一个字符是花色
suit = tile[-1]
if suit in suit_count:
suit_count[suit] += 1
total_tiles = len(hand)
weights = {}
for suit in suits:
if suit == missing_suit:
weights[suit] = 0.0
else:
count = suit_count[suit]
# 计算数量比例
ratio = count / total_tiles if total_tiles > 0 else 0
# 估算胡牌概率(简化版)
win_prob = min(1.0, count / 12.0) # 简化估计
# 计算权重
weights[suit] = alpha * ratio + beta * win_prob
return weights
```
### 测试
```python
# 测试用例
hand = ['1万', '2万', '3万', '4筒', '5筒', '6筒', '7条']
missing_suit = '万' # 缺万
weights = analyze_colors(hand, missing_suit)
print(weights) # 预期:万:0, 筒和条有权重
```
### 说明
1. 这个简化版本没有考虑已经打出的牌和对手的手牌,因此胡牌概率的估计非常粗略。
2. 在实际应用中,`P_win^f`应该通过更复杂的模型(如基于概率表、机器学习模型或蒙特卡洛模拟)来估算。
### 改进方向
1. 可以引入剩余牌的信息,从而更准确地估算胡牌概率。
2. 考虑花色的组合潜力,比如该花色是否容易形成顺子(如相邻牌的数量)或刻子。
### 相关问题
1. 如何更精确地估算麻将AI中的胡牌概率?
2. 花色权重分析中如何考虑对手的弃牌信息?
3. 在动态调整攻防平衡系数(alpha和beta)时,有哪些策略?</think># 麻将AI花色权重分析功能实现
根据四川麻将规则和决策优化需求,我将实现麻将AI决策流程中的花色权重分析功能。核心功能是动态计算各花色权重,帮助AI做出最优决策。
## 实现方案
```python
class ColorAnalyzer:
def __init__(self, hand, discarded_tiles, round_stage):
"""
花色权重分析器
:param hand: 当前手牌列表,如["1万", "2万", "3筒", "4条"]
:param discarded_tiles: 已弃牌列表
:param round_stage: 牌局阶段(0-1),0=早期,1=晚期
"""
self.hand = hand
self.discarded_tiles = discarded_tiles
self.round_stage = round_stage
self.suits = ['万', '筒', '条'] # 四川麻将三种花色
def analyze_colors(self):
"""主分析函数:计算各花色权重"""
# 1. 确定缺失花色
missing_suit = self._determine_missing_suit()
# 2. 计算各花色权重
weights = {}
for suit in self.suits:
if suit == missing_suit:
weights[suit] = 0.0 # 缺失花色权重为0
else:
weights[suit] = self._calculate_suit_weight(suit)
return weights, missing_suit
def _determine_missing_suit(self):
"""确定缺失花色(根据手牌数量最少的花色)"""
suit_counts = {suit: 0 for suit in self.suits}
for tile in self.hand:
for suit in self.suits:
if suit in tile:
suit_counts[suit] += 1
return min(suit_counts, key=suit_counts.get)
def _calculate_suit_weight(self, suit):
"""计算单个花色权重"""
# 计算该花色牌的数量和比例
suit_tiles = [t for t in self.hand if suit in t]
suit_count = len(suit_tiles)
total_tiles = len(self.hand)
suit_ratio = suit_count / total_tiles if total_tiles > 0 else 0
# 估算胡牌概率(简化版)
win_prob = self._estimate_win_probability(suit, suit_count)
# 动态调整攻防平衡系数
alpha, beta = self._adjust_balance_coefficients()
# 计算最终权重
return alpha * suit_ratio + beta * win_prob
def _estimate_win_probability(self, suit, suit_count):
"""
估算该花色的胡牌概率(简化版)
实际应用中应使用更精确的概率模型[^1]
"""
# 基础概率:基于该花色牌的数量
base_prob = min(1.0, suit_count / 8.0) # 8张牌可组成2个面子+将牌
# 调整因子:考虑该花色的安全性和潜力
safety_factor = self._calculate_safety_factor(suit)
potential_factor = self._calculate_potential_factor(suit)
return base_prob * safety_factor * potential_factor
def _calculate_safety_factor(self, suit):
"""计算安全系数:基于该花色在弃牌中的比例"""
total_discarded = len(self.discarded_tiles)
if total_discarded == 0:
return 1.0
suit_discarded = sum(1 for t in self.discarded_tiles if suit in t)
discard_ratio = suit_discarded / total_discarded
# 弃牌率越高,安全系数越高
return min(1.0, discard_ratio * 1.5) # 放大安全系数
def _calculate_potential_factor(self, suit):
"""计算潜力系数:评估该花色形成组合的潜力"""
# 获取该花色所有牌的数字
suit_numbers = [int(t[0]) for t in self.hand if suit in t]
if not suit_numbers:
return 0.5 # 无牌时默认值
# 计算形成顺子/刻子的潜力
potential = 0
# 刻子潜力:相同数字的牌
from collections import Counter
num_counts = Counter(suit_numbers)
for count in num_counts.values():
if count >= 2: # 有对子或更多
potential += 0.2 * min(count, 3) # 最多0.6
# 顺子潜力:连续数字的牌
sorted_nums = sorted(set(suit_numbers))
for i in range(len(sorted_nums) - 1):
diff = sorted_nums[i+1] - sorted_nums[i]
if diff == 1: # 连续
potential += 0.15
elif diff == 2: # 间隔1
potential += 0.1
return min(1.0, 0.5 + potential) # 基础0.5 + 潜力值
def _adjust_balance_coefficients(self):
"""动态调整攻防平衡系数α和β"""
# 基础系数
alpha = 0.6 # 数量比例权重
beta = 0.4 # 胡牌概率权重
# 根据牌局阶段调整:晚期更注重胡牌概率
if self.round_stage > 0.7: # 晚期阶段
alpha -= 0.2 * self.round_stage
beta += 0.2 * self.round_stage
# 根据手牌进攻性调整(简化)
if len(self.hand) < 10: # 手牌较少时更注重胡牌
alpha = max(0.4, alpha - 0.1)
beta = min(0.6, beta + 0.1)
return max(0.1, alpha), max(0.1, beta)
```
## 使用示例
```python
# 示例数据
hand = ["1万", "2万", "3万", "4筒", "5筒", "6筒", "7条", "8条"]
discarded = ["3万", "4万", "5条", "6条"]
round_stage = 0.4 # 中期阶段
# 创建分析器
analyzer = ColorAnalyzer(hand, discarded, round_stage)
# 执行分析
weights, missing_suit = analyzer.analyze_colors()
print(f"缺失花色: {missing_suit}")
print("花色权重:")
for suit, weight in weights.items():
print(f"{suit}: {weight:.3f}")
```
## 关键算法说明
### 1. 缺失花色确定
使用最小牌数原则确定缺失花色:
$$ \text{missing\_suit} = \argmin_{s \in \{\text{万}, \text{筒}, \text{条}\}} |H_s| $$
其中 $H_s$ 是手牌中花色 $s$ 的牌数
### 2. 花色权重计算
对于非缺失花色,权重计算公式为:
$$ W_f = \alpha \cdot \frac{|H_f|}{|H|} + \beta \cdot P_{\text{win}}^f $$
其中:
- $\alpha$ 和 $\beta$ 是动态调整的攻防平衡系数
- $|H_f|$ 是该花色牌的数量
- $|H|$ 是手牌总数
- $P_{\text{win}}^f$ 是该花色的胡牌概率估计值
### 3. 胡牌概率估计
使用多因子模型简化计算:
$$ P_{\text{win}}^f = \underbrace{\min\left(1, \frac{|H_f|}{8}\right)}_{\text{基础概率}} \times \underbrace{S_f}_{\text{安全系数}} \times \underbrace{P_f}_{\text{潜力系数}} $$
### 4. 平衡系数动态调整
根据牌局阶段调整攻防策略:
$$ \alpha_t = \alpha_0 - 0.2 \cdot \delta_t $$
$$ \beta_t = \beta_0 + 0.2 \cdot \delta_t $$
其中 $\delta_t$ 是牌局阶段因子(0=早期,1=晚期)
## 性能优化建议
1. **缓存机制**:对于相同手牌状态,缓存计算结果
2. **概率表预计算**:提前计算常见牌型的胡牌概率
3. **并行计算**:各花色权重计算可并行执行
4. **增量更新**:手牌变化时只更新受影响的花色
## 相关扩展问题
1. 如何将花色权重分析与其他决策模块(如单牌价值计算)集成?
可设计权重传递机制:$$ V_{\text{final}} = V_{\text{base}} \times W_f \times V_{\text{safe}} $$
其中 $W_f$ 是牌 $f$ 所属花色的权重[^1]
2. 在估算胡牌概率时,如何更精确地考虑剩余牌分布?
可使用马尔可夫链模型跟踪各花色剩余牌数量:$$ P(\text{win}|H_f) = \sum_{k=0}^{4} P(\text{摸到}k\text{张牌}) \times P(\text{胡牌}|H_f+k) $$
3. 如何根据对手行为动态调整安全系数?
可引入对手弃牌模式分析:$$ S_f = 1 - \frac{\sum_{i=1}^{3} \text{对手}i\text{弃牌中}f\text{的比例}}{3} $$
4. 花色权重分析如何适应不同麻将变种规则?
可通过规则参数化实现:$$ W_f = \begin{cases}
0 & \text{缺一门规则} \\
\alpha \cdot \frac{|H_f|}{|H|} + \beta \cdot P_{\text{win}}^f & \text{标准规则} \\
\text{特殊计算} & \text{其他变种}
\end{cases} $$