import tkinter as tk
from tkinter import messagebox, font
import random
import os
class Card:
def __init__(self, suit, rank, value):
self.suit = suit
self.rank = rank
self.value = value
self.image_path = f"{suit}_{rank}.png" # 实际使用时需要准备对应的图片
def __str__(self):
return f"{self.suit}{self.rank}"
class Player:
def __init__(self, name):
self.name = name
self.cards = []
self.is_landlord = False
def sort_cards(self):
self.cards.sort(key=lambda card: card.value)
def play_cards(self, selected_indices):
selected_cards = [self.cards[i] for i in sorted(selected_indices, reverse=True)]
for i in sorted(selected_indices, reverse=True):
del self.cards[i]
return selected_cards
class LandlordsGame:
def __init__(self):
self.players = [Player("玩家"), Player("电脑1"), Player("电脑2")]
self.deck = []
self.bottom_cards = []
self.current_player_index = 0
self.last_played_cards = []
self.last_player_index = -1
self.landlord_index = -1
self.setup_deck()
def setup_deck(self):
suits = ["♠", "♥", "♣", "♦"]
ranks = ["3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2"]
values = list(range(3, 16)) # 3-15
# 普通牌
for suit in suits:
for i, rank in enumerate(ranks):
self.deck.append(Card(suit, rank, values[i]))
# 大小王
self.deck.append(Card("", "小王", 16))
self.deck.append(Card("", "大王", 17))
def shuffle_deck(self):
random.shuffle(self.deck)
def deal_cards(self):
self.shuffle_deck()
self.bottom_cards = self.deck[-3:]
for i in range(17):
for player in self.players:
player.cards.append(self.deck.pop(0))
for player in self.players:
player.sort_cards()
def select_landlord(self, player_index):
self.landlord_index = player_index
self.players[player_index].is_landlord = True
self.players[player_index].cards.extend(self.bottom_cards)
self.players[player_index].sort_cards()
self.current_player_index = player_index
self.last_player_index = player_index
def is_valid_play(self, played_cards):
# 简化的牌型判断,实际需要完整实现各种牌型规则
if not played_cards:
return self.last_played_cards or self.current_player_index == self.last_player_index
if not self.last_played_cards:
return True # 第一个出牌,只要有牌就行
# 这里应该实现完整的牌型比较逻辑
return True
def play_round(self, player_index, played_cards):
if not self.is_valid_play(played_cards):
return False
self.last_played_cards = played_cards
self.last_player_index = player_index
if not self.players[player_index].cards:
return True # 游戏结束
# 轮到下一个玩家
self.current_player_index = (self.current_player_index + 1) % 3
# 如果当前玩家选择不出
if not played_cards and self.current_player_index == self.last_player_index:
self.last_played_cards = [] # 重新开始一轮
return False
class LandlordsGUI:
def __init__(self, root):
self.root = root
self.root.title("斗地主")
self.root.geometry("1200x700")
self.root.configure(bg="#008000") # 绿色背景模拟牌桌
self.game = LandlordsGame()
self.selected_indices = set()
self.buttons_frame = None
self.player_hand_frame = None
self.computer1_frame = None
self.computer2_frame = None
self.table_frame = None
self.bottom_cards_frame = None
self.card_font = font.Font(family="SimHei", size=14)
self.title_font = font.Font(family="SimHei", size=24, weight="bold")
self.setup_ui()
def setup_ui(self):
# 标题
title_label = tk.Label(self.root, text="斗地主", bg="#008000", fg="white", font=self.title_font)
title_label.pack(pady=20)
# 主游戏区域
main_frame = tk.Frame(self.root, bg="#008000")
main_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=10)
# 电脑2区域(顶部)
self.computer2_frame = tk.Frame(main_frame, bg="#008000")
self.computer2_frame.pack(fill=tk.X, pady=10)
self.computer2_label = tk.Label(self.computer2_frame, text="电脑2", bg="#008000", fg="white",
font=self.card_font)
self.computer2_label.pack()
self.computer2_cards_frame = tk.Frame(self.computer2_frame, bg="#008000")
self.computer2_cards_frame.pack(pady=5)
# 桌面区域(中间)
self.table_frame = tk.Frame(main_frame, bg="#008000")
self.table_frame.pack(fill=tk.BOTH, expand=True, pady=20)
# 底牌区域
self.bottom_cards_frame = tk.Frame(self.table_frame, bg="#008000")
self.bottom_cards_frame.pack(side=tk.TOP, pady=10)
self.bottom_cards_label = tk.Label(self.bottom_cards_frame, text="底牌", bg="#008000", fg="white",
font=self.card_font)
self.bottom_cards_label.pack()
self.bottom_cards_display = tk.Frame(self.bottom_cards_frame, bg="#008000")
self.bottom_cards_display.pack(pady=5)
# 已出牌区域
self.played_cards_frame = tk.Frame(self.table_frame, bg="#008000")
self.played_cards_frame.pack(side=tk.TOP, pady=20)
self.played_cards_label = tk.Label(self.played_cards_frame, text="已出牌", bg="#008000", fg="white",
font=self.card_font)
self.played_cards_label.pack()
self.played_cards_display = tk.Frame(self.played_cards_frame, bg="#008000")
self.played_cards_display.pack(pady=5)
# 玩家区域(底部)
self.player_hand_frame = tk.Frame(main_frame, bg="#008000")
self.player_hand_frame.pack(side=tk.BOTTOM, fill=tk.X, pady=10)
self.player_label = tk.Label(self.player_hand_frame, text="玩家", bg="#008000", fg="white", font=self.card_font)
self.player_label.pack()
self.player_cards_frame = tk.Frame(self.player_hand_frame, bg="#008000")
self.player_cards_frame.pack(pady=5)
# 电脑1区域(右侧)
self.computer1_frame = tk.Frame(main_frame, bg="#008000")
self.computer1_frame.pack(side=tk.RIGHT, fill=tk.Y, padx=10)
self.computer1_label = tk.Label(self.computer1_frame, text="电脑1", bg="#008000", fg="white",
font=self.card_font)
self.computer1_label.pack()
self.computer1_cards_frame = tk.Frame(self.computer1_frame, bg="#008000")
self.computer1_cards_frame.pack(pady=5)
# 按钮区域
self.buttons_frame = tk.Frame(self.root, bg="#008000")
self.buttons_frame.pack(pady=20)
# 开始游戏按钮
self.start_button = tk.Button(self.buttons_frame, text="开始游戏", command=self.start_game, font=self.card_font)
self.start_button.pack(side=tk.LEFT, padx=10)
# 出牌按钮
self.play_button = tk.Button(self.buttons_frame, text="出牌", command=self.play_selected_cards,
font=self.card_font, state=tk.DISABLED)
self.play_button.pack(side=tk.LEFT, padx=10)
# 不出按钮
self.pass_button = tk.Button(self.buttons_frame, text="不出", command=self.pass_turn, font=self.card_font,
state=tk.DISABLED)
self.pass_button.pack(side=tk.LEFT, padx=10)
# 叫地主按钮
self.landlord_button = tk.Button(self.buttons_frame, text="叫地主", command=lambda: self.select_landlord(True),
font=self.card_font, state=tk.DISABLED)
self.landlord_button.pack(side=tk.LEFT, padx=10)
# 不叫按钮
self.not_landlord_button = tk.Button(self.buttons_frame, text="不叫",
command=lambda: self.select_landlord(False), font=self.card_font,
state=tk.DISABLED)
self.not_landlord_button.pack(side=tk.LEFT, padx=10)
def start_game(self):
self.game = LandlordsGame()
self.game.deal_cards()
self.selected_indices = set()
self.update_display()
# 显示叫地主按钮
self.start_button.config(state=tk.DISABLED)
self.landlord_button.config(state=tk.NORMAL)
self.not_landlord_button.config(state=tk.NORMAL)
self.play_button.config(state=tk.DISABLED)
self.pass_button.config(state=tk.DISABLED)
# 显示底牌背面
self.update_bottom_cards_display(hidden=True)
# 提示玩家叫地主
messagebox.showinfo("游戏开始", "游戏已开始,请选择是否叫地主")
def update_display(self):
# 清空所有显示区域
for widget in self.player_cards_frame.winfo_children():
widget.destroy()
for widget in self.computer1_cards_frame.winfo_children():
widget.destroy()
for widget in self.computer2_cards_frame.winfo_children():
widget.destroy()
for widget in self.played_cards_display.winfo_children():
widget.destroy()
# 显示玩家手牌
for i, card in enumerate(self.game.players[0].cards):
card_label = tk.Label(self.player_cards_frame, text=f"{card.suit}{card.rank}",
bg="white", fg="black", relief=tk.RAISED,
bd=2, padx=10, pady=15, font=self.card_font)
card_label.grid(row=0, column=i, padx=2)
card_label.bind("<Button-1>", lambda event, idx=i: self.toggle_card_selection(idx))
# 显示电脑1手牌(背面)
for i in range(len(self.game.players[1].cards)):
card_back = tk.Label(self.computer1_cards_frame, text="背面",
bg="red", fg="white", relief=tk.RAISED,
bd=2, padx=5, pady=10, font=self.card_font)
card_back.pack(pady=1)
# 显示电脑2手牌(背面)
for i in range(len(self.game.players[2].cards)):
card_back = tk.Label(self.computer2_cards_frame, text="背面",
bg="red", fg="white", relief=tk.RAISED,
bd=2, padx=10, pady=5, font=self.card_font)
card_back.grid(row=0, column=i, padx=1)
# 显示已出牌
for i, card in enumerate(self.game.last_played_cards):
card_label = tk.Label(self.played_cards_display, text=f"{card.suit}{card.rank}",
bg="white", fg="black", relief=tk.RAISED,
bd=2, padx=10, pady=15, font=self.card_font)
card_label.grid(row=0, column=i, padx=2)
# 更新玩家标签显示当前回合
if self.game.landlord_index != -1:
player_labels = [self.player_label, self.computer1_label, self.computer2_label]
for i, label in enumerate(player_labels):
text = self.game.players[i].name
if self.game.players[i].is_landlord:
text += "(地主)"
if i == self.game.current_player_index:
text += " [当前回合]"
label.config(text=text)
def update_bottom_cards_display(self, hidden=False):
for widget in self.bottom_cards_display.winfo_children():
widget.destroy()
if hidden:
for i in range(3):
card_back = tk.Label(self.bottom_cards_display, text="背面",
bg="red", fg="white", relief=tk.RAISED,
bd=2, padx=10, pady=15, font=self.card_font)
card_back.grid(row=0, column=i, padx=2)
else:
for i, card in enumerate(self.game.bottom_cards):
card_label = tk.Label(self.bottom_cards_display, text=f"{card.suit}{card.rank}",
bg="white", fg="black", relief=tk.RAISED,
bd=2, padx=10, pady=15, font=self.card_font)
card_label.grid(row=0, column=i, padx=2)
def toggle_card_selection(self, index):
if self.game.current_player_index != 0:
return # 不是玩家回合
if index in self.selected_indices:
self.selected_indices.remove(index)
# 更新卡片外观
for i, widget in enumerate(self.player_cards_frame.winfo_children()):
if i == index:
widget.config(bg="white", fg="black")
else:
self.selected_indices.add(index)
# 更新卡片外观
for i, widget in enumerate(self.player_cards_frame.winfo_children()):
if i == index:
widget.config(bg="yellow", fg="black")
def select_landlord(self, is_landlord):
if is_landlord:
self.game.select_landlord(0)
self.update_bottom_cards_display(hidden=False)
messagebox.showinfo("地主确定", "你成为了地主!")
else:
# 简化处理,电脑随机选择一个当地主
landlord_index = random.choice([1, 2])
self.game.select_landlord(landlord_index)
self.update_bottom_cards_display(hidden=False)
messagebox.showinfo("地主确定", f"{self.game.players[landlord_index].name}成为了地主!")
# 更新显示
self.update_display()
# 隐藏叫地主按钮,显示游戏按钮
self.landlord_button.config(state=tk.DISABLED)
self.not_landlord_button.config(state=tk.DISABLED)
# 如果轮到玩家
if self.game.current_player_index == 0:
self.play_button.config(state=tk.NORMAL)
self.pass_button.config(state=tk.NORMAL)
else:
self.play_button.config(state=tk.DISABLED)
self.pass_button.config(state=tk.DISABLED)
# 电脑出牌
self.root.after(1000, self.computer_play)
def play_selected_cards(self):
if not self.selected_indices:
messagebox.showwarning("提示", "请选择要出的牌!")
return
selected_cards = [self.game.players[0].cards[i] for i in self.selected_indices]
if not self.game.is_valid_play(selected_cards):
messagebox.showwarning("提示", "出牌不符合规则!")
return
# 出牌
game_over = self.game.play_round(0, selected_cards)
self.selected_indices = set()
self.update_display()
if game_over:
messagebox.showinfo("游戏结束", "恭喜,你赢了!")
self.start_button.config(state=tk.NORMAL)
self.play_button.config(state=tk.DISABLED)
self.pass_button.config(state=tk.DISABLED)
return
# 轮到电脑
self.play_button.config(state=tk.DISABLED)
self.pass_button.config(state=tk.DISABLED)
self.root.after(1000, self.computer_play)
def pass_turn(self):
game_over = self.game.play_round(0, []) # 不出牌
self.selected_indices = set()
self.update_display()
if game_over:
messagebox.showinfo("游戏结束", f"{self.game.players[self.game.last_player_index].name}赢了!")
self.start_button.config(state=tk.NORMAL)
self.play_button.config(state=tk.DISABLED)
self.pass_button.config(state=tk.DISABLED)
return
# 轮到电脑
self.play_button.config(state=tk.DISABLED)
self.pass_button.config(state=tk.DISABLED)
self.root.after(1000, self.computer_play)
def computer_play(self):
current_player = self.game.players[self.game.current_player_index]
# 简单AI:随机出牌或不出
if self.game.last_player_index == self.game.current_player_index or random.random() > 0.3:
# 出牌
if self.game.last_played_cards:
# 跟牌逻辑,简化处理
played_cards = [current_player.cards[0]] # 只出一张最小的牌
else:
# 先手出牌,随机出1-5张牌
num_cards = random.randint(1, min(5, len(current_player.cards)))
played_cards = current_player.cards[:num_cards]
# 从手牌中移除已出的牌
for card in played_cards:
current_player.cards.remove(card)
self.game.last_played_cards = played_cards
self.game.last_player_index = self.game.current_player_index
messagebox.showinfo("出牌", f"{current_player.name}出了{len(played_cards)}张牌")
else:
# 不出
self.game.last_played_cards = []
messagebox.showinfo("出牌", f"{current_player.name}选择不出")
# 检查游戏是否结束
if not current_player.cards:
self.update_display()
messagebox.showinfo("游戏结束", f"{current_player.name}赢了!")
self.start_button.config(state=tk.NORMAL)
return
# 轮到下一个玩家
self.game.current_player_index = (self.game.current_player_index + 1) % 3
self.update_display()
# 如果轮到玩家
if self.game.current_player_index == 0:
self.play_button.config(state=tk.NORMAL)
self.pass_button.config(state=tk.NORMAL)
else:
# 继续电脑出牌
self.root.after(1000, self.computer_play)
if __name__ == "__main__":
root = tk.Tk()
# 确保中文显示正常
font.families() # 加载字体
app = LandlordsGUI(root)
root.mainloop()
Python3.8.10每日一练(斗地主)
最新推荐文章于 2025-10-16 10:16:50 发布