Files
drawing-app/baohuang_game.py
T

568 lines
18 KiB
Python

"""
山东保皇游戏
经典的 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()