wsy182 2024-12-01 19:29:42 +08:00
parent bf1c5116be
commit 27da1caf44
4 changed files with 101 additions and 114 deletions

View File

@ -2,6 +2,7 @@ import random as random_module
from loguru import logger
from src.engine.calculate_fan import calculate_fan
from src.engine.mahjong_tile import MahjongTile
@ -233,20 +234,23 @@ def handle_peng(self, player, tile):
"""
处理玩家碰牌逻辑并更新出牌顺序
"""
if not isinstance(tile, MahjongTile):
logger.error(f"玩家 {player} 碰牌的牌无效: {tile}")
return False
if self.state.hands[player].tile_count[tile] < 2:
logger.error(f"玩家 {player} 无法碰牌: {tile}")
return False
# 减少两张牌
self.state.hands[player].tile_count[tile] -= 2
self.state.melds[player].append(("", tile))
# 更新状态
self._update_meld(player, tile, "", count=2)
logger.info(f"玩家 {player} 碰了牌: {tile}。当前明牌: {self.state.melds[player]}")
return True
def get_player_discard_choice(self, player):
"""
模拟获取玩家打牌的选择
@ -269,24 +273,26 @@ def handle_gang(self, player, tile, mode):
:param mode: 杠牌的类型 ("ming" "an")
:return: True 如果操作成功否则 False
"""
if not isinstance(tile, MahjongTile):
logger.error(f"玩家 {player} 杠牌的牌无效: {tile}")
return False
base_score = self.state.bottom_score # 底分
if mode == "ming": # 明杠逻辑
if self.state.hands[player].tile_count[tile] < 3:
logger.error(f"玩家 {player} 无法明杠: {tile}")
return None
return False
# 减少牌数量并更新明牌
self.state.hands[player].tile_count[tile] -= 3
self.state.melds[player].append(("", tile))
logger.info(f"玩家 {player} 明杠了牌: {tile}")
# 更新状态
self._update_meld(player, tile, "", count=3)
# 明杠分数计算
gang_score = base_score * 2 # 明杠的分数倍率
gang_score = base_score * 2
for i in range(4):
if i != player:
self.state.scores[i] -= gang_score # 每个其他玩家扣分
self.state.scores[player] += gang_score * 3 # 杠牌玩家得总分
self.state.scores[i] -= gang_score
self.state.scores[player] += gang_score * 3
logger.info(f"玩家 {player} 明杠,总分变化: +{gang_score * 3},其他玩家每人扣分: -{gang_score}")
return True
@ -296,21 +302,28 @@ def handle_gang(self, player, tile, mode):
logger.error(f"玩家 {player} 无法暗杠: {tile}")
return False
# 减少牌数量并更新暗牌
self.state.hands[player].tile_count[tile] -= 4
self.state.melds[player].append(("an_gang", tile))
logger.info(f"玩家 {player} 暗杠了牌: {tile}")
# 更新状态
update_meld(player, tile, "", count=4)
# 暗杠分数计算
gang_score = base_score * 4 # 暗杠的分数倍率
gang_score = base_score * 4
for i in range(4):
if i != player:
self.state.scores[i] -= gang_score # 每个其他玩家扣分
self.state.scores[player] += gang_score * 3 # 杠牌玩家得总分
self.state.scores[i] -= gang_score
self.state.scores[player] += gang_score * 3
logger.info(f"玩家 {player} 暗杠,总分变化: +{gang_score * 3},其他玩家每人扣分: -{gang_score}")
return True
def update_meld(self, player, tile, meld_type, count):
"""
更新玩家的明牌状态并移除相应的牌
"""
self.state.hands[player].tile_count[tile] -= count
self.state.melds[player].append((meld_type, tile))
logger.info(f"玩家 {player} 更新明牌: {meld_type} {tile},当前明牌: {self.state.melds[player]}")
def random_discard_tile(engine):
"""
@ -417,27 +430,40 @@ def select_discard_tile(self, player):
def handle_win(self, player, current_player, tile):
"""
处理胡牌逻辑包括判断地胡计算番数和分数
处理胡牌逻辑包括动态计算番数和分数
:param player: 胡牌玩家索引
:param current_player: 打出牌的玩家索引
:param current_player: 打出牌的玩家索引若自摸为 None
:param tile: 胡牌的那张牌
"""
logger.info(f"玩家 {player} 胡牌!胡的牌是: {tile}")
logger.info(f"玩家 {player} 胡牌!胡的牌是: {tile if tile else '自摸'}")
# 判断是否地胡
is_dihu = self.state.draw_counts[player] == 0 and player != self.state.current_player
if is_dihu:
logger.info(f"玩家 {player} 地胡!")
else:
logger.info(f"玩家 {player} 不是地胡")
is_self_draw = current_player is None
# 计算分数
fan_count = 5 # 按你逻辑固定番数为5
base_score = self.state.bottom_score # 底分
# 动态计算番数
fan_count = calculate_fan(
hand=self.state.hands[player],
melds=self.state.melds[player],
is_self_draw=is_self_draw,
winning_tile=tile,
conditions={"is_tian_hu": False, "is_di_hu": is_dihu}
)
# 分数计算
base_score = self.state.bottom_score
win_score = base_score * (2 ** fan_count)
self.state.scores[player] += win_score * 3 # 胡牌玩家得总分
self.state.scores[current_player] -= win_score # 点炮玩家扣除分数
if is_self_draw:
# 自摸结算
for i in range(4):
if i != player:
self.state.scores[i] -= win_score
self.state.scores[player] += win_score * 3
else:
# 点炮结算
self.state.scores[player] += win_score * 3
self.state.scores[current_player] -= win_score
# 更新赢家状态
self.state.winners.append(player)
@ -446,6 +472,15 @@ def handle_win(self, player, current_player, tile):
# 输出日志
logger.info(f"玩家 {player} 胡牌类型: {'地胡' if is_dihu else '普通胡牌'}")
logger.info(f"玩家 {player} 总番数: {fan_count}")
logger.info(f"玩家 {current_player} 点炮,扣分: {win_score}")
logger.info(f"玩家 {current_player if current_player is not None else '所有其他玩家'} 扣分: {win_score}")
logger.info(f"玩家 {player} 加分: {win_score * 3}")
logger.info(f"当前分数: {self.state.scores}")
def _update_meld(self, player, tile, meld_type, count):
"""
更新玩家的明牌状态并移除相应的牌
"""
self.state.hands[player].tile_count[tile] -= count
self.state.melds[player].append((meld_type, tile))
logger.info(f"玩家 {player} 更新明牌: {meld_type} {tile},当前明牌: {self.state.melds[player]}")

View File

@ -1,92 +1,44 @@
def calculate_fan(hand, melds, is_self_draw, is_cleared, conditions):
from src.engine.fan_type import is_terminal_fan,is_cleared,is_full_request,is_seven_pairs,is_basic_win,is_dragon_seven_pairs
from loguru import logger
def calculate_fan(hand, melds, is_self_draw, winning_tile, conditions):
"""
动态计算番数优先处理高番数
动态计算番数根据现有的番型规则
参数:
- hand: 当前胡牌的手牌Hand 对象
- melds: 玩家已明牌的列表Meld 对象列表
- is_self_draw: 是否自摸
- is_cleared: 是否清一色
- conditions: 字典包含特殊胡牌条件 {"is_seven_pairs": True, "is_tian_hu": True}
- winning_tile: 胡的那张牌MahjongTile 对象
- conditions: 字典包含特殊胡牌条件 {"is_tian_hu": True}
返回:
- fan: 最大番数
- int: 最大番数
"""
fan = 0 # 默认番数
fan = 0 # 初始化番数
# 定义番数规则(按优先级从高到低排序)
rules = {
"tian_hu": lambda: 12 if conditions.get("is_tian_hu", False) else 0, # 天胡
"di_hu": lambda: 12 if conditions.get("is_di_hu", False) else 0, # 地胡
"dragon_seven_pairs": lambda: 12 if is_dragon_seven_pairs(hand) else 0, # 龙七对
"clear_seven_pairs": lambda: 12 if is_cleared and is_seven_pairs(hand) else 0, # 清七对
"full_request": lambda: 6 if is_full_request(hand, melds) else 0, # 全求人
"pure_cleared": lambda: 4 if is_cleared and len(melds) >= 2 else 0, # 极品清一色
"seven_pairs": lambda: 2 if is_seven_pairs(hand) else 0, # 七对
"big_pairs": lambda: 2 if is_big_pairs(hand) else 0, # 大对子
"small_pairs": lambda: 2 if is_small_pairs(hand) else 0, # 小七对
"golden_hook": lambda: 1 if is_golden_hook(hand) else 0, # 金钩吊
"gang_flower": lambda: 1 if conditions.get("is_gang_flower", False) else 0, # 杠上开花
"rob_gang": lambda: 1 if conditions.get("is_rob_gang", False) else 0, # 抢杠胡
"under_the_sea": lambda: 1 if conditions.get("is_under_the_sea", False) else 0, # 海底捞月
"plain_win": lambda: 1 if not any(conditions.values()) else 0 # 平胡
}
# 定义番型规则(按优先级从高到低排序)
rules = [
("tian_hu", lambda: 12 if conditions.get("is_tian_hu", False) else 0), # 天胡
("di_hu", lambda: 12 if conditions.get("is_di_hu", False) else 0), # 地胡
("dragon_seven_pairs", lambda: is_dragon_seven_pairs(hand, melds)[0]), # 龙七对
("seven_pairs", lambda: is_seven_pairs(hand)), # 七对
("full_request", lambda: is_full_request(hand, melds, winning_tile)), # 全求人
("cleared", lambda: is_cleared(hand, melds)), # 清一色
("terminal_fan", lambda: is_terminal_fan(hand, melds)), # 带幺九
("plain_win", lambda: is_basic_win(hand)), # 平胡
]
# 逐一应用规则
for rule, func in rules.items():
result = func()
if result > fan:
fan = result # 选择当前最大番数
# 逐一应用规则,取最大番数
for rule, func in rules:
current_fan = func()
if current_fan > fan:
fan = current_fan
logger.debug(f"应用番型规则 {rule}: {current_fan}")
# 特殊条件(例如自摸加番)
if is_self_draw:
fan += 1 # 自摸额外加 1 番
logger.debug("自摸加 1 番")
return fan
# 辅助函数实现
def is_seven_pairs(hand):
"""检查是否是七对(七个对子)。"""
from collections import Counter
counter = Counter(hand.tiles)
return sum(1 for count in counter.values() if count == 2) == 7
def is_dragon_seven_pairs(hand):
"""检查是否是龙七对(七对中有一对是三张)。"""
from collections import Counter
counter = Counter(hand.tiles)
pairs = sum(1 for count in counter.values() if count == 2)
triplets = sum(1 for count in counter.values() if count == 3)
return pairs == 6 and triplets == 1
def is_big_pairs(hand):
"""检查是否是大对子(四坎牌和一对将,坎牌为刻子)。"""
from collections import Counter
counter = Counter(hand.tiles)
triplets = sum(1 for count in counter.values() if count == 3)
pairs = sum(1 for count in counter.values() if count == 2)
return triplets == 4 and pairs == 1
def is_golden_hook(hand):
"""检查是否是金钩吊(只剩一张牌)。"""
return len(hand.tiles) == 1
def is_full_request(hand, melds):
"""检查是否是全求人(所有牌通过碰、杠完成)。"""
return all(tile_count == 0 for tile_count in hand.tiles) and len(melds) > 0
def is_cleared(hand, melds):
"""检查是否是清一色(所有牌同一种花色)。"""
all_tiles = hand.tiles + [meld.tile for meld in melds]
suits = {tile.suit for tile in all_tiles}
return len(suits) == 1
def is_small_pairs(hand):
"""
检查是否是小七对六对加一对
"""
from collections import Counter
counter = Counter(hand.tiles)
pairs = sum(1 for count in counter.values() if count == 2)
return pairs == 6

View File

@ -40,7 +40,7 @@ def is_cleared(hand, melds):
return 0
def calculate_terminal_fan(hand, melds):
def is_terminal_fan(hand, melds):
"""
计算带幺九番型并返回对应番数
"""

View File

@ -1,6 +1,6 @@
from src.engine.hand import Hand
from src.engine.mahjong_tile import MahjongTile
from src.engine.fan_type import is_basic_win,is_cleared,calculate_terminal_fan,is_seven_pairs,is_full_request,is_dragon_seven_pairs
from src.engine.fan_type import is_basic_win,is_cleared,is_terminal_fan,is_seven_pairs,is_full_request,is_dragon_seven_pairs
from src.engine.meld import Meld
def test_is_basic_win():
@ -100,7 +100,7 @@ def test_calculate_terminal_fan():
hand.add_tile(MahjongTile("", 5))
hand.add_tile(MahjongTile("", 5))
melds = []
assert calculate_terminal_fan(hand, melds) == 3, "测试失败:基本带幺九应为 3 番"
assert is_terminal_fan(hand, melds) == 3, "测试失败:基本带幺九应为 3 番"
def test_is_seven_pairs():
"""测试七对番型"""