Add 山东保皇游戏 - 5 人经典扑克游戏
This commit is contained in:
@@ -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()
|
||||
Reference in New Issue
Block a user