""" 山东保皇游戏 经典的 5 人扑克游戏 """ import random from enum import Enum from typing import List, Dict, Tuple, Optional class Suit(Enum): """花色枚举""" SPADES = "♠" # 黑桃 HEARTS = "♥" # 红心 CLUBS = "♣" # 梅花 DIAMONDS = "♦" # 方块 JOKER = "🃏" # 王 class CardValue(Enum): """牌值枚举""" THREE = 3 FOUR = 4 FIVE = 5 SIX = 6 SEVEN = 7 EIGHT = 8 NINE = 9 TEN = 10 JACK = 11 QUEEN = 12 KING = 13 ACE = 14 TWO = 15 SMALL_JOKER = 16 # 小王 BIG_JOKER = 17 # 大王 EMPEROR = 18 # 皇帝牌(带标记的大王) GUARD = 19 # 侍卫牌(带标记的小王) class Card: """扑克牌类""" def __init__(self, value: CardValue, suit: Suit = None): self.value = value self.suit = suit self.display = self._get_display() def _get_display(self) -> str: """获取牌的显示文本""" value_map = { CardValue.THREE: '3', CardValue.FOUR: '4', CardValue.FIVE: '5', CardValue.SIX: '6', CardValue.SEVEN: '7', CardValue.EIGHT: '8', CardValue.NINE: '9', CardValue.TEN: '10', CardValue.JACK: 'J', CardValue.QUEEN: 'Q', CardValue.KING: 'K', CardValue.ACE: 'A', CardValue.TWO: '2', CardValue.SMALL_JOKER: '小王', CardValue.BIG_JOKER: '大王', CardValue.EMPEROR: '皇', CardValue.GUARD: '卫', } if self.value in [CardValue.SMALL_JOKER, CardValue.BIG_JOKER, CardValue.EMPEROR, CardValue.GUARD]: return value_map[self.value] suit_str = self.suit.value if self.suit else '' return f"{suit_str}{value_map[self.value]}" def __str__(self): return self.display def __repr__(self): return self.display def power(self) -> int: """获取牌力值""" # 侍卫牌 > 皇帝牌 > 大王 > 小王 > 2 > A > ... > 3 power_map = { CardValue.GUARD: 22, CardValue.EMPEROR: 21, CardValue.BIG_JOKER: 20, CardValue.SMALL_JOKER: 19, CardValue.TWO: 18, CardValue.ACE: 17, CardValue.KING: 16, CardValue.QUEEN: 15, CardValue.JACK: 14, CardValue.TEN: 13, CardValue.NINE: 12, CardValue.EIGHT: 11, CardValue.SEVEN: 10, CardValue.SIX: 9, CardValue.FIVE: 8, CardValue.FOUR: 7, CardValue.THREE: 6, } return power_map.get(self.value, 0) class CardType(Enum): """牌型枚举""" SINGLE = "单张" PAIR = "对子" TRIPLE = "三张" BOMB = "炸弹" SEQUENCE = "顺子" PASS = "不要" class Hand: """手牌类""" def __init__(self): self.cards: List[Card] = [] def add_card(self, card: Card): """添加牌""" self.cards.append(card) def remove_cards(self, cards: List[Card]): """移除牌""" for card in cards: if card in self.cards: self.cards.remove(card) def sort(self): """按牌力排序""" self.cards.sort(key=lambda c: c.power(), reverse=True) def is_empty(self) -> bool: """是否为空""" return len(self.cards) == 0 def card_count(self) -> int: """牌的数量""" return len(self.cards) def get_display(self) -> str: """获取手牌显示""" return ' '.join([str(c) for c in self.cards]) class Player: """玩家类""" def __init__(self, name: str, player_id: int): self.name = name self.player_id = player_id self.hand = Hand() self.role: str = "" # "皇帝", "侍卫", "革命党" self.is_emperor = False self.is_guard = False self.finished_position: int = 0 # 完成位置(第几个出完牌) def __str__(self): return f"{self.name}({self.role})" class GameRule: """游戏规则类""" @staticmethod def get_card_type(cards: List[Card]) -> Tuple[CardType, int]: """ 判断牌型 Returns: (牌型,牌力值) """ if not cards: return (CardType.PASS, 0) count = len(cards) # 单张 if count == 1: return (CardType.SINGLE, cards[0].power()) # 对子 if count == 2 and cards[0].value == cards[1].value: return (CardType.PAIR, cards[0].power()) # 三张 if count == 3 and all(c.value == cards[0].value for c in cards): return (CardType.TRIPLE, cards[0].power()) # 炸弹(4 张相同) if count == 4 and all(c.value == cards[0].value for c in cards): return (CardType.BOMB, cards[0].power() + 100) # 炸弹牌力加 100 # 顺子(至少 5 张连续) if count >= 5: values = sorted([c.power() for c in cards]) is_sequence = all(values[i+1] - values[i] == 1 for i in range(len(values)-1)) if is_sequence: return (CardType.SEQUENCE, values[0]) return (CardType.PASS, 0) @staticmethod def can_beat(played_cards: List[Card], new_cards: List[Card]) -> bool: """ 判断是否能打过 Args: played_cards: 已出的牌 new_cards: 新出的牌 Returns: True 表示可以打过 """ if not played_cards: return True # 首家出牌 played_type, played_power = GameRule.get_card_type(played_cards) new_type, new_power = GameRule.get_card_type(new_cards) if new_type == CardType.PASS: return False # 牌数必须相同 if len(played_cards) != len(new_cards): return False # 炸弹可以打非炸弹 if new_type == CardType.BOMB and played_type != CardType.BOMB: return True # 同牌型比较牌力 if new_type == played_type: return new_power > played_power return False class BaohuangGame: """保皇游戏类""" def __init__(self): """初始化游戏""" self.players: List[Player] = [] self.deck: List[Card] = [] self.current_player_idx: int = 0 self.last_played_cards: List[Card] = [] self.last_played_player_idx: int = -1 self.game_started = False self.game_over = False self.emperor_idx: int = -1 self.guard_idx: int = -1 # 创建牌堆(2 副牌) self._create_deck() def _create_deck(self): """创建牌堆(2 副牌,共 108 张)""" self.deck = [] # 普通牌(2 副) for _ in range(2): for suit in [Suit.SPADES, Suit.HEARTS, Suit.CLUBS, Suit.DIAMONDS]: for value in [CardValue.THREE, CardValue.FOUR, CardValue.FIVE, CardValue.SIX, CardValue.SEVEN, CardValue.EIGHT, CardValue.NINE, CardValue.TEN, CardValue.JACK, CardValue.QUEEN, CardValue.KING, CardValue.ACE, CardValue.TWO]: self.deck.append(Card(value, suit)) # 小王(2 张,其中 1 张是侍卫牌) self.deck.append(Card(CardValue.SMALL_JOKER)) self.deck.append(Card(CardValue.GUARD)) # 侍卫牌 # 大王(2 张,其中 1 张是皇帝牌) self.deck.append(Card(CardValue.BIG_JOKER)) self.deck.append(Card(CardValue.EMPEROR)) # 皇帝牌 print(f"🃏 牌堆创建完成,共{len(self.deck)}张牌") def setup_players(self, player_names: List[str] = None): """ 设置玩家 Args: player_names: 玩家名字列表(默认 5 人) """ if player_names is None: player_names = [f"玩家{i+1}" for i in range(5)] elif len(player_names) != 5: print("❌ 保皇需要 5 人游戏") return self.players = [] for i, name in enumerate(player_names): player = Player(name, i) self.players.append(player) print(f"👥 玩家设置完成:{', '.join([p.name for p in self.players])}") def deal_cards(self): """发牌""" print("\n🔄 开始发牌...") # 洗牌 random.shuffle(self.deck) # 发牌(每人 21 张,剩余 3 张底牌) card_idx = 0 for player in self.players: for _ in range(21): if card_idx < len(self.deck): player.hand.add_card(self.deck[card_idx]) card_idx += 1 # 剩余 3 张作为底牌(给皇帝) self.base_cards = self.deck[card_idx:] print(f"📦 底牌:{len(self.base_cards)}张") # 排序手牌 for player in self.players: player.hand.sort() print("✅ 发牌完成") def identify_roles(self): """ 确定身份 抓到皇帝牌的是皇帝,抓到侍卫牌的是侍卫 """ print("\n🎭 确定身份...") for player in self.players: for card in player.hand.cards: if card.value == CardValue.EMPEROR: player.is_emperor = True player.role = "皇帝" self.emperor_idx = player.player_id print(f"👑 {player.name} 是皇帝(持有皇帝牌)") if card.value == CardValue.GUARD: player.is_guard = True player.role = "侍卫" self.guard_idx = player.player_id print(f"🛡️ {player.name} 是侍卫(持有侍卫牌)") # 设置革命党 for player in self.players: if not player.is_emperor and not player.is_guard: player.role = "革命党" # 皇帝获得底牌 if self.emperor_idx >= 0 and hasattr(self, 'base_cards'): emperor = self.players[self.emperor_idx] for card in self.base_cards: emperor.hand.add_card(card) emperor.hand.sort() print(f"\n📦 皇帝{emperor.name}获得底牌") def get_current_player(self) -> Player: """获取当前玩家""" return self.players[self.current_player_idx] def play_cards(self, player_idx: int, cards: List[Card]) -> bool: """ 出牌 Args: player_idx: 玩家索引 cards: 要出的牌 Returns: True 表示出牌成功 """ player = self.players[player_idx] # 检查是否是该玩家出牌 if player_idx != self.current_player_idx: print(f"❌ 还没到{player.name}出牌") return False # 检查手牌中是否有这些牌 for card in cards: if card not in player.hand.cards: print(f"❌ {player.name}没有这张牌:{card}") return False # 检查是否能打过上家 if self.last_played_cards and self.last_played_player_idx != player_idx: if not GameRule.can_beat(self.last_played_cards, cards): print(f"❌ {cards}打不过{self.last_played_cards}") return False # 出牌 player.hand.remove_cards(cards) self.last_played_cards = cards self.last_played_player_idx = player_idx print(f"🎴 {player.name} 出牌:{' '.join([str(c) for c in cards])}") # 检查是否出完 if player.hand.is_empty(): player.finished_position = len([p for p in self.players if p.finished_position > 0]) + 1 print(f"🎉 {player.name} 出完牌了!第{player.finished_position}名") # 检查游戏是否结束 if player.finished_position >= 3: # 前 3 名出完就结束 self.game_over = True # 下一个玩家 self.current_player_idx = (self.current_player_idx + 1) % len(self.players) # 跳过已出完牌的玩家 while self.players[self.current_player_idx].hand.is_empty(): self.current_player_idx = (self.current_player_idx + 1) % len(self.players) # 如果是新的回合,重置出牌 if self.current_player_idx == self.last_played_player_idx: self.last_played_cards = [] self.last_played_player_idx = -1 return True def pass_turn(self, player_idx: int): """ 过牌 Args: player_idx: 玩家索引 """ player = self.players[player_idx] if player_idx != self.current_player_idx: print(f"❌ 还没到{player.name}出牌") return if not self.last_played_cards or self.last_played_player_idx == player_idx: print(f"❌ {player.name}不能过牌(是首家)") return print(f"⏭️ {player.name} 过牌") # 下一个玩家 self.current_player_idx = (self.current_player_idx + 1) % len(self.players) # 跳过已出完牌的玩家 while self.players[self.current_player_idx].hand.is_empty(): self.current_player_idx = (self.current_player_idx + 1) % len(self.players) def check_winner(self) -> str: """ 检查胜负 Returns: 获胜方 """ if not self.game_over: return "" # 统计皇帝方和革命党的名次 emperor_team_positions = [] revolutionary_positions = [] for player in self.players: if player.is_emperor or player.is_guard: emperor_team_positions.append(player.finished_position) else: revolutionary_positions.append(player.finished_position) # 皇帝方获胜条件:皇帝和侍卫都进前 3,或者皇帝第 1 且侍卫不是最后 emperor_wins = False if all(pos <= 3 for pos in emperor_team_positions): emperor_wins = True elif 1 in emperor_team_positions and 5 not in emperor_team_positions: emperor_wins = True if emperor_wins: return "皇帝方获胜" else: return "革命党获胜" def display_game_state(self): """显示游戏状态""" print("\n" + "=" * 60) print("🎮 游戏状态") print("=" * 60) for i, player in enumerate(self.players): marker = "👑" if player.is_emperor else ("🛡️" if player.is_guard else "") finished = f"(第{player.finished_position}名)" if player.finished_position > 0 else "" print(f"{i+1}. {marker}{player.name} [{player.role}] - 剩余{player.hand.card_count()}张 {finished}") if self.last_played_cards and self.last_played_player_idx >= 0: last_player = self.players[self.last_played_player_idx] print(f"\n🎴 上家出牌:{last_player.name} - {' '.join([str(c) for c in self.last_played_cards])}") current = self.get_current_player() print(f"\n➡️ 当前出牌:{current.name}") print("=" * 60) def run(self): """运行游戏主流程""" try: print("\n" + "=" * 60) print("🃏 山东保皇 🃏") print("=" * 60) # 设置玩家 self.setup_players() # 发牌 self.deal_cards() # 确定身份 self.identify_roles() # 游戏主循环 self.game_started = True round_num = 0 while not self.game_over: round_num += 1 print(f"\n{'='*60}") print(f"第 {round_num} 轮") print(f"{'='*60}") self.display_game_state() # 简单的 AI 出牌(示例) current_player = self.get_current_player() # 模拟出牌(实际应该由玩家输入) if current_player.hand.cards: # 出最小的单张 card_to_play = [current_player.hand.cards[-1]] self.play_cards(current_player.player_id, card_to_play) else: self.pass_turn(current_player.player_id) # 游戏结束 print("\n" + "=" * 60) print("🎊 游戏结束!") print("=" * 60) for player in self.players: print(f"{player.name}: 第{player.finished_position}名") winner = self.check_winner() print(f"\n🏆 {winner}") print("=" * 60) except KeyboardInterrupt: print("\n\n游戏被中断!再见!👋\n") def main(): """主函数""" game = BaohuangGame() game.run() if __name__ == "__main__": main()