

Python,速成心法
敲代码,查资料,问度娘
练习,探索,总结,优化

★★★★★博文创作不易,我的博文不需要打赏,也不需要知识付费,可以白嫖学习编程小技巧。使用代码的过程中,如有疑问的地方,欢迎大家指正留言交流。喜欢的老铁可以多多点赞+收藏分享+置顶,小红牛在此表示感谢。★★★★★
---------★Pygame教程★---------
Python经典游戏:太空飞机大战Space Plane Battle
Pygame经典游戏:坦克大战TankWar+五子棋人机对弈+俄罗斯方块(安排!!)
Pygame经典游戏:微信飞机大战Wechatflying(初级版)
Python经典游戏:走迷宫,好烦呀,我到现在还没有走出去!!
Pygame教程03:文本显示+字体加载+transform方法
Pygame教程04:draw方法绘制矩形、多边形、圆、椭圆、弧线、直线和线条等
Pygame教程05:帧动画原理+边界值检测,让小球来回上下运动
Pygame教程06:Event事件的类型+处理方法+监听鼠标事件
Pygame教程08:使用键盘方向键,控制小球,上下左右移动。
Pygame教程09:font.render文本内容,如何自动换行显示
以下是您需要的斗地主游戏Python实现。它包含了完整的牌型判断、出牌规则和简单的AI对手,您可以直接在命令行中运行并开始游戏。以下是您需要的斗地主游戏Python实现。它包含了完整的牌型判断、出牌规则和简单的AI对手,您可以直接在命令行中运行并开始游戏。

↓ 源码如下 ↓
# -*- coding: utf-8 -*-# @Author : 小红牛# 微信公众号:wdPythonimport randomfrom typing import List, Tuple, Optional, Any# ---------- 牌的定义 ----------RANK_MAP = {'3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9, '10': 10,'J': 11, 'Q': 12, 'K': 13, 'A': 14, '2': 15, '小王': 16, '大王': 17}VALUE_TO_RANK = {v: k for k, v in RANK_MAP.items()}SUITS = ['♠', '♥', '♣', '♦']class Card:def __init__(self, rank: str, suit: str = ''):self.rank = rankself.suit = suitself.value = RANK_MAP[rank]def __repr__(self):return f"{self.suit}{self.rank}" if self.suit else self.rankdef __str__(self):return self.__repr__()def __lt__(self, other):return self.value < other.value# ---------- 牌型判断 ----------def get_card_type(cards: List[Card]):"""返回 (类型字符串, 比较键) 如果非法返回 ('invalid', None)"""if not cards:return 'invalid', None# 按点数排序sorted_cards = sorted(cards, key=lambda c: c.value)values = [c.value for c in sorted_cards]# 统计点数出现次数count_dict = {}for v in values:count_dict[v] = count_dict.get(v, 0) + 1n = len(cards)# 1. 火箭if n == 2 and 16 in count_dict and 17 in count_dict:return 'rocket', 100# 2. 炸弹 (纯四张)bombs = [v for v, cnt in count_dict.items() if cnt == 4]if len(bombs) == 1 and n == 4:return 'bomb', bombs[0]# 3. 四带二 (四张 + 两张单或一对 或 两对)if len(bombs) == 1:bomb_val = bombs[0]remaining = [v for v in values if v != bomb_val]if len(remaining) == 2:if remaining[0] == remaining[1]:return 'four_with_pair', bomb_val # 带一对else:return 'four_with_single', bomb_val # 带两张单elif len(remaining) == 4:# 检查是否为两对rem_count = {}for v in remaining:rem_count[v] = rem_count.get(v, 0) + 1if all(cnt == 2 for cnt in rem_count.values()) and len(rem_count) == 2:return 'four_with_two_pairs', bomb_val# 4. 三张相关 (三张, 三带一, 三带一对, 飞机)trips = [v for v, cnt in count_dict.items() if cnt == 3]if trips:# 单三张if len(trips) == 1 and n == 3:return 'triple', trips[0]# 三带一if len(trips) == 1 and n == 4:return 'three_with_one', trips[0]# 三带一对if len(trips) == 1 and n == 5:remaining = [v for v in values if v != trips[0]]if len(remaining) == 2 and remaining[0] == remaining[1]:return 'three_with_pair', trips[0]# 飞机 (多个连续三张)if len(trips) >= 2:trips_sorted = sorted(trips)# 不能包含2或王if all(v < 15 for v in trips_sorted): # 2=15, 王更大# 检查连续性is_consecutive = all(trips_sorted[i+1] - trips_sorted[i] == 1 for i in range(len(trips_sorted)-1))if is_consecutive:triple_cnt = len(trips)remaining_vals = [v for v in values if v not in trips]rem_n = len(remaining_vals)# 不带翼if rem_n == 0:return 'airplane', (trips_sorted[0], triple_cnt)# 带单张翅膀if rem_n == triple_cnt:rem_count = {}for v in remaining_vals:rem_count[v] = rem_count.get(v, 0) + 1if all(cnt == 1 for cnt in rem_count.values()):return 'airplane_with_single', (trips_sorted[0], triple_cnt)# 带对子翅膀if rem_n == 2 * triple_cnt:rem_count = {}for v in remaining_vals:rem_count[v] = rem_count.get(v, 0) + 1if all(cnt == 2 for cnt in rem_count.values()) and len(rem_count) == triple_cnt:return 'airplane_with_pair', (trips_sorted[0], triple_cnt)# 5. 顺子 (5张以上连续单张)if n >= 5 and all(cnt == 1 for cnt in count_dict.values()):sorted_vals = sorted(count_dict.keys())if max(sorted_vals) <= 14 and min(sorted_vals) >= 3: # 不含2和王if all(sorted_vals[i+1] - sorted_vals[i] == 1 for i in range(len(sorted_vals)-1)):return 'straight', (sorted_vals[0], n)# 6. 连对 (3对以上连续对子)if n >= 6 and n % 2 == 0 and all(cnt == 2 for cnt in count_dict.values()):sorted_vals = sorted(count_dict.keys())if max(sorted_vals) <= 14 and min(sorted_vals) >= 3:if all(sorted_vals[i+1] - sorted_vals[i] == 1 for i in range(len(sorted_vals)-1)):return 'straight_pair', (sorted_vals[0], n // 2)# 7. 单张if n == 1:return 'single', values[0]# 8. 对子if n == 2 and values[0] == values[1]:return 'pair', values[0]return 'invalid', Nonedef can_beat(last_type: str, last_key: Any, cur_type: str, cur_key: Any) -> bool:"""判断当前牌是否能压过上家"""# 火箭无敌if cur_type == 'rocket':return Trueif last_type == 'rocket':return False# 炸弹处理if cur_type == 'bomb' and last_type != 'bomb':return Trueif last_type == 'bomb' and cur_type != 'bomb':return Falseif last_type == 'bomb' and cur_type == 'bomb':return cur_key > last_key# 不同类型不可比if cur_type != last_type:return False# 相同类型比较键if isinstance(last_key, tuple) and isinstance(cur_key, tuple):# 顺子、连对、飞机等需要长度相同if last_key[1] != cur_key[1]:return Falsereturn cur_key[0] > last_key[0]else:return cur_key > last_key# ---------- 玩家类 ----------class Player:def __init__(self, name: str):self.name = nameself.hand: List[Card] = []self.is_landlord = Falsedef add_cards(self, cards: List[Card]):self.hand.extend(cards)def remove_cards(self, cards: List[Card]):for card in cards:self.hand.remove(card)def show_hand(self) -> str:sorted_hand = sorted(self.hand)return ' '.join(str(c) for c in sorted_hand)# ---------- 游戏类 ----------class Game:def __init__(self, player_names: List[str]):self.players = [Player(name) for name in player_names]self.deck = self._create_deck()self.bottom_cards: List[Card] = []self.landlord_index: Optional[int] = Noneself.current_player_index: int = 0self.last_play: Optional[Tuple[int, List[Card], str, Any]] = None # (玩家索引, 出的牌, 类型, 键)self.pass_count: int = 0def _create_deck(self) -> List[Card]:deck = []for rank, val in RANK_MAP.items():if rank in ('小王', '大王'):deck.append(Card(rank))else:for suit in SUITS:deck.append(Card(rank, suit))random.shuffle(deck)return deckdef deal_cards(self):"""发牌,每人17张,留3张底牌"""for i in range(3):for j in range(17):self.players[j % 3].add_cards([self.deck.pop()])self.bottom_cards = self.deckself.deck = []def choose_landlord(self):"""随机选择地主,并获得底牌"""self.landlord_index = random.randint(0, 2)self.players[self.landlord_index].is_landlord = Trueself.players[self.landlord_index].add_cards(self.bottom_cards)self.current_player_index = self.landlord_indexprint(f"\n🎲 地主是 {self.players[self.landlord_index].name},底牌: {', '.join(str(c) for c in self.bottom_cards)}")def next_player(self):self.current_player_index = (self.current_player_index + 1) % 3def play_round(self):"""进行一轮出牌,直到有人获胜"""while True:player = self.players[self.current_player_index]print(f"\n--- 轮到 {player.name} ---")print(f"手牌: {player.show_hand()}")if self.last_play is None:print("上一手牌: 无")else:last_player = self.players[self.last_play[0]]cards_str = ' '.join(str(c) for c in self.last_play[1])print(f"上一手牌: {last_player.name} 出了 {cards_str}")# 玩家行动action = self._player_action(player)if action is None: # 过print(f"{player.name} 不要")self.pass_count += 1if self.pass_count >= 2: # 连续两人不要,本轮结束,下一人重新出牌self.last_play = Noneself.pass_count = 0else:cards, ctype, key = actionplayer.remove_cards(cards)print(f"{player.name} 出了: {' '.join(str(c) for c in cards)}")self.last_play = (self.current_player_index, cards, ctype, key)self.pass_count = 0if not player.hand:print(f"\n🏆 {player.name} 获胜!游戏结束。")returnself.next_player()def _player_action(self, player: Player):"""根据玩家类型调用不同策略"""if player.name == "玩家":return self._human_action(player)else:return self._ai_action(player)def _human_action(self, player: Player):"""人类玩家通过命令行输入出牌"""while True:inp = input("请输入要出的牌(点数,空格分隔)或输入 'pass' 过:").strip()if inp.lower() == 'pass':if self.last_play is None:print("现在是你先出牌,不能过")continuereturn Noneranks = inp.split()selected = []hand_copy = player.hand[:] # 用于查找,避免重复使用同一张牌for r in ranks:found = Falsefor card in hand_copy:if card.rank == r:selected.append(card)hand_copy.remove(card)found = Truebreakif not found:print(f"手牌中没有 {r},请重新输入")breakelse:# 所有牌都在手牌中ctype, key = get_card_type(selected)if ctype == 'invalid':print("牌型不合法,请重新出牌")continueif self.last_play is None:return selected, ctype, keyelse:if can_beat(self.last_play[2], self.last_play[3], ctype, key):return selected, ctype, keyelse:print("不能压过上家,请重新出牌")continuedef _ai_action(self, player: Player):"""简单AI:枚举所有合法出牌,选择能压过的最小牌;否则过"""hand = player.handif not hand:return None# 枚举所有非空子集,找出合法牌型def all_plays(hand):plays = []n = len(hand)for mask in range(1, 1 << n):cards = [hand[i] for i in range(n) if (mask >> i) & 1]ctype, key = get_card_type(cards)if ctype != 'invalid':plays.append((cards, ctype, key))return playsif self.last_play is None:# 先出牌:优先出单张,否则随便出plays = all_plays(hand)# 找单张for play in plays:if play[1] == 'single':return playreturn plays[0] if plays else Noneelse:last_type, last_key = self.last_play[2], self.last_play[3]plays = all_plays(hand)beats = [p for p in plays if can_beat(last_type, last_key, p[1], p[2])]if beats:# 简单选第一个(可优化为选最小)return beats[0]else:return None# ---------- 主程序 ----------if __name__ == "__main__":print("🎴 欢迎来斗地主!")game = Game(["玩家", "AI1", "AI2"])game.deal_cards()game.choose_landlord()game.play_round()
完毕!!感谢您的收看
--------★★历史博文集合★★--------
