From 64c7b47a4bbb1c8922dedd3abaf26c74d5684db5 Mon Sep 17 00:00:00 2001 From: wangsiyuan <2392948297@qq.com> Date: Sun, 1 Dec 2024 05:36:23 +0800 Subject: [PATCH] Update actions.py --- src/engine/actions.py | 162 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 143 insertions(+), 19 deletions(-) diff --git a/src/engine/actions.py b/src/engine/actions.py index be0cabc..8309d2c 100644 --- a/src/engine/actions.py +++ b/src/engine/actions.py @@ -1,22 +1,24 @@ +from random import random + from loguru import logger from src.engine.mahjong_tile import MahjongTile def draw_tile(engine): """ - 当前玩家摸牌逻辑,记录牌的详细信息和游戏状态。 + 当前玩家摸牌逻辑,按照顺序从牌堆顶部摸牌,并记录牌的详细信息和游戏状态。 """ # 检查牌堆是否已空 if engine.state.remaining_tiles == 0: logger.warning("牌堆已空,游戏结束!") engine.game_over = True - return 0, True # 游戏结束时返回 0 和 done = True + return None, True # 游戏结束时返回 None 和 done = True # 当前玩家 current_player = engine.state.current_player - # 从牌堆中摸一张牌 - tile = engine.state.deck.pop(0) # 从牌堆抽取一张牌 + # 从牌堆顶部摸一张牌 + tile = engine.state.deck.pop(0) # 按顺序从牌堆取出一张牌 engine.state.remaining_tiles -= 1 # 更新剩余牌数 engine.state.hands[current_player].add_tile(tile) # 将牌对象加入当前玩家手牌 @@ -32,12 +34,8 @@ def draw_tile(engine): if tile.suit == missing_suit: logger.info(f"玩家 {current_player} 摸到缺门牌: {tile_name},需要优先打出") - # 切换到下一位玩家 - next_player = (current_player + 1) % 4 - engine.state.current_player = next_player - - # 返回奖励和游戏是否结束的标志 - return 0, False # 奖励为 0,done 为 False(游戏继续) + # 返回摸到的牌和游戏是否结束的标志 + return tile, False # 返回摸到的牌对象和游戏继续的标志 def discard_tile(self, tile): """ @@ -192,9 +190,10 @@ def set_missing_suit(player, game_state): def check_other_players(self, tile): """ 检查其他玩家是否可以对打出的牌进行操作(如碰、杠、胡牌)。 + 如果有玩家选择碰或杠,修改游戏状态和出牌顺序。 """ current_player = self.state.current_player - next_player = (current_player + 1) % 4 + actions_taken = False for player in range(4): if player == current_player: @@ -203,20 +202,145 @@ def check_other_players(self, tile): # 检查是否可以碰 if self.state.hands[player].tile_count[tile] >= 2: logger.info(f"玩家 {player} 可以碰玩家 {current_player} 的牌: {tile}") - # 根据规则决定是否碰 - # 这里可以扩展为调用 AI 或玩家输入 - # self.peng(tile) + if self.handle_peng(player, tile): # 执行碰牌逻辑 + actions_taken = True + break # 碰牌后不检查其他玩家 # 检查是否可以杠 if self.state.hands[player].tile_count[tile] >= 3: logger.info(f"玩家 {player} 可以杠玩家 {current_player} 的牌: {tile}") - # 根据规则决定是否杠 - # self.gang(tile, mode="ming") + if self.handle_gang(player, tile, mode="ming"): # 执行明杠逻辑 + actions_taken = True + break # 杠牌后不检查其他玩家 # 检查是否可以胡牌 if self.can_win(self.state.hands[player], self.state.melds[player], self.state.missing_suits[player]): logger.info(f"玩家 {player} 可以胡玩家 {current_player} 的牌: {tile}") - # 根据规则决定是否胡牌 - # self.handle_win(player) + self.handle_win(player, current_player, tile) + actions_taken = True + break # 胡牌后结束 - logger.info(f"玩家 {current_player} 打出的牌 {tile} 没有触发其他玩家的操作") \ No newline at end of file + if not actions_taken: + logger.info(f"玩家 {current_player} 打出的牌 {tile} 没有触发其他玩家的操作") + +def handle_peng(self, player, tile): + """ + 处理玩家碰牌逻辑并更新出牌顺序。 + """ + 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(("peng", tile)) + + logger.info(f"玩家 {player} 碰了牌: {tile}。当前明牌: {self.state.melds[player]}") + + # 设置出牌顺序为碰牌的玩家 + self.state.current_player = player + + # 提供玩家手牌信息,等待玩家选择打出的牌 + chosen_tile = self.get_player_discard_choice(player) + + # 验证玩家选择的牌是否合法 + if chosen_tile not in self.state.hands[player].tiles: + logger.error(f"玩家 {player} 选择了不合法的牌: {chosen_tile}") + raise ValueError("打出的牌必须存在于玩家的手牌中") + + # 玩家选择打出这张牌 + self.discard_tile(player, chosen_tile) + return True + +def get_player_discard_choice(self, player): + """ + 模拟获取玩家打牌的选择。 + 在实际项目中,使用 GUI 或 AI 决策替代。 + """ + hand_tiles = self.state.hands[player].tiles + logger.info(f"玩家 {player} 当前手牌: {[str(tile) for tile in hand_tiles]}") + + # 模拟玩家选择打出第一张非碰牌 + chosen_tile = hand_tiles[0] # 在真实项目中替换为实际用户输入或AI决策 + logger.info(f"玩家 {player} 选择打出牌: {chosen_tile}") + return chosen_tile + +def handle_gang(self, player, tile, mode): + """ + 处理玩家杠牌逻辑并更新状态。 + """ + if mode == "ming" and self.state.hands[player].tile_count[tile] < 3: + logger.error(f"玩家 {player} 无法明杠: {tile}") + return False + + # 减少牌数量并更新明牌 + if mode == "ming": + self.state.hands[player].tile_count[tile] -= 3 + self.state.melds[player].append(("ming_gang", tile)) + logger.info(f"玩家 {player} 明杠了牌: {tile}") + elif mode == "an": + if self.state.hands[player].tile_count[tile] < 4: + 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}") + + # 杠牌后玩家补摸一张牌 + self.draw_tile_for_player(player) + return True + +def random_discard_tile(engine): + """ + 当前玩家随机选择一张牌打出,优先打缺门牌。 + """ + current_player = engine.state.current_player + hand = engine.state.hands[current_player].tiles # 当前玩家的手牌 + missing_suit = engine.state.missing_suits[current_player] # 当前玩家的缺门 + + if not hand: + logger.error(f"玩家 {current_player} 的手牌为空,无法打牌") + return + + # 使用改进后的 random_choice 函数选择牌 + tile = random_choice(hand, missing_suit) + + # 从手牌中移除该牌 + engine.state.hands[current_player].remove_tile(tile) + engine.state.discards[current_player].append(tile) + + logger.info( + f"玩家 {current_player} 打出了一张牌: {tile}。" + f"当前牌河: {[str(t) for t in engine.state.discards[current_player]]}" + ) + + # 检查其他玩家是否可以操作 + engine.check_other_players(tile) + + # 切换到下一个玩家 + next_player = (current_player + 1) % 4 + engine.state.current_player = next_player + + +def random_choice(hand, missing_suit): + """ + 根据缺门优先随机选择打出的牌。 + + :param hand: 当前玩家的手牌(列表,包含 MahjongTile 对象)。 + :param missing_suit: 当前玩家的缺门花色(字符串)。 + :return: 随机选择的 MahjongTile 对象。 + """ + if not hand: + raise ValueError("手牌不能为空") + + # 筛选出缺门的牌 + missing_suit_tiles = [tile for tile in hand if tile.suit == missing_suit] + + # 如果缺门牌存在,优先从缺门牌中随机选择 + if missing_suit_tiles: + index = random.randint(0, len(missing_suit_tiles) - 1) + return missing_suit_tiles[index] + + # 如果缺门牌不存在,从剩余牌中随机选择 + index = random.randint(0, len(hand) - 1) + return hand[index] \ No newline at end of file