diff --git a/baohuang_game.py b/baohuang_game.py new file mode 100644 index 0000000..d592b7d --- /dev/null +++ b/baohuang_game.py @@ -0,0 +1,567 @@ +""" +山东保皇游戏 +经典的 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() diff --git a/play_baohuang.py b/play_baohuang.py new file mode 100644 index 0000000..fa39fca --- /dev/null +++ b/play_baohuang.py @@ -0,0 +1,8 @@ +""" +山东保皇游戏启动文件 +""" + +from baohuang_game import main + +if __name__ == "__main__": + main()