Python+tkinter实现俄罗斯方块游戏

游戏界面:

一个方块区域(游戏板),显示当前分数和最高分的标签,以及控制按钮(重启、暂停、显示排行榜)。玩家可以通过点击"Restart"按钮重启游戏,点击"Pause"按钮暂停游戏,点击"Show Leaderboard"按钮查看排行榜(最高的十个分数)。

游戏规则

玩家需要通过移动和旋转从屏幕顶部掉落的方块,使它们在屏幕底部堆积成完整的行。 游戏中有7种不同形状的方块:I形、O形、T形、S形、Z形、L形和J形,每种形状都有不同的颜色。

  • 使用键盘方向键来控制方块的移动和旋转:

    • 左箭头键:向左移动方块。
    • 右箭头键:向右移动方块。
    • 下箭头键:向下移动方块。
    • 上箭头键:旋转方块。
  • 当方块堆积到屏幕顶部,无法再放置新的方块时,游戏结束。

  • 每清除一行,玩家会获得100分。游戏结束时,当前分数会保存到文件中,如果当前分数超过最高分,则更新最高分。

游戏代码

python 复制代码
import tkinter as tk
import random
import os

# 游戏配置
CELL_SIZE = 35  # 每个方块的大小
COLUMNS = 10  # 游戏板的列数
ROWS = 20  # 游戏板的行数
INITIAL_DELAY = 500  # 初始延迟时间(毫秒)
DELAY = 500  # 每次更新的延迟时间(毫秒)
SCORE_FILE = "scores.txt"  # 存储分数的文件

# 方块形状
SHAPES = [
    [[1, 1, 1, 1]],  # I 形
    [[1, 1], [1, 1]],  # O 形
    [[0, 1, 0], [1, 1, 1]],  # T 形
    [[1, 1, 0], [0, 1, 1]],  # S 形
    [[0, 1, 1], [1, 1, 0]],  # Z 形
    [[1, 0, 0], [1, 1, 1]],  # L 形
    [[0, 0, 1], [1, 1, 1]]  # J 形
]

# 方块颜色
COLORS = ["cyan", "yellow", "purple", "green", "red", "orange", "blue"]

class Tetris:
    def __init__(self, root):
        self.root = root
        self.score = 0  # 当前分数
        self.delay = INITIAL_DELAY
        self.high_score = self.get_high_score()  # 最高分

        # 创建一个单独的框架来放置按钮
        self.button_frame = tk.Frame(root)
        self.button_frame.pack(side=tk.TOP, pady=10)

        # 创建并放置排行榜按钮
        self.leaderboard_button = tk.Button(self.button_frame, text="Show Leaderboard", command=self.show_leaderboard)
        self.leaderboard_button.pack(side=tk.LEFT, padx=0)  # 将排行榜按钮放在页面顶端
        self.leaderboard_button.pack_forget()  # 初始化时隐藏排行榜按钮

        # 创建并放置重启按钮
        self.restart_button = tk.Button(self.button_frame, text="Restart", command=self.restart_game)
        self.restart_button.pack(side=tk.LEFT, padx=0)  # 将重启按钮放在页面顶端

        # 创建并放置暂停按钮
        self.pause_button = tk.Button(self.button_frame, text="Pause", command=self.toggle_pause)
        self.pause_button.pack(side=tk.LEFT, padx=0)  # 将暂停按钮放在页面顶端

        self.score_label = tk.Label(root, text=f"Score: {self.score}  High Score: {self.high_score}")
        self.score_label.pack()
        self.canvas = tk.Canvas(root, width=COLUMNS*CELL_SIZE, height=ROWS*CELL_SIZE)
        self.canvas.pack()
        self.game_over = False  # 游戏结束标志
        self.paused = False  # 游戏暂停标志
        self.board = [[0] * COLUMNS for _ in range(ROWS)]  # 初始化游戏板
        self.current_shape = self.new_shape()  # 当前方块形状
        self.current_position = [0, COLUMNS // 2 - len(self.current_shape[0]) // 2]  # 当前方块位置
        self.root.bind("<Key>", self.key_press)  # 绑定键盘事件
        self.update()  # 开始游戏更新循环

    def new_shape(self):
        """生成一个新的方块形状"""
        shape = random.choice(SHAPES)  # 随机选择一个方块形状
        color = COLORS[SHAPES.index(shape)]  # 获取对应的颜色
        return [[(cell and color) for cell in row] for row in shape]  # 返回带颜色的方块形状

    def draw_board(self):
        """绘制游戏板和当前方块"""
        self.canvas.delete("all")  # 清空画布
        for r in range(ROWS):
            for c in range(COLUMNS):
                if self.board[r][c]:
                    self.canvas.create_rectangle(c*CELL_SIZE, r*CELL_SIZE, (c+1)*CELL_SIZE, (r+1)*CELL_SIZE, fill=self.board[r][c])
                self.canvas.create_rectangle(c*CELL_SIZE, r*CELL_SIZE, (c+1)*CELL_SIZE, (r+1)*CELL_SIZE, outline="gray")
        for r, row in enumerate(self.current_shape):
            for c, cell in enumerate(row):
                if cell:
                    self.canvas.create_rectangle((self.current_position[1]+c)*CELL_SIZE, (self.current_position[0]+r)*CELL_SIZE, (self.current_position[1]+c+1)*CELL_SIZE, (self.current_position[0]+r+1)*CELL_SIZE, fill=cell)

    def move(self, delta_row, delta_col):
        """移动当前方块"""
        new_position = [self.current_position[0] + delta_row, self.current_position[1] + delta_col]
        if self.valid_position(new_position):
            self.current_position = new_position
            return True
        return False

    def rotate(self):
        """旋转当前方块"""
        new_shape = list(zip(*self.current_shape[::-1]))  # 顺时针旋转方块
        if self.valid_position(self.current_position, new_shape):
            self.current_shape = new_shape

    def valid_position(self, position, shape=None):
        """检查方块在给定位置是否有效"""
        if shape is None:
            shape = self.current_shape
        for r, row in enumerate(shape):
            for c, cell in enumerate(row):
                if cell:
                    if r + position[0] >= ROWS or c + position[1] < 0 or c + position[1] >= COLUMNS or self.board[r + position[0]][c + position[1]]:
                        return False
        return True

    def freeze(self):
        """冻结当前方块并生成新方块"""
        for r, row in enumerate(self.current_shape):
            for c, cell in enumerate(row):
                if cell:
                    self.board[self.current_position[0] + r][self.current_position[1] + c] = cell
        self.clear_lines()
        self.current_shape = self.new_shape()
        self.current_position = [0, COLUMNS // 2 - len(self.current_shape[0]) // 2]
        if not self.valid_position(self.current_position):
            self.game_over = True

    def clear_lines(self):
        """清除已填满的行并更新分数"""
        new_board = [row for row in self.board if any(cell == 0 for cell in row)]
        lines_cleared = ROWS - len(new_board)
        self.board = [[0] * COLUMNS for _ in range(lines_cleared)] + new_board
        self.score += lines_cleared * 100  # 每消除一行增加100分
        self.score_label.config(text=f"Score: {self.score}  High Score: {self.high_score}")

    def update(self):
        """更新游戏状态"""
        if not self.game_over and not self.paused:
            if not self.move(1, 0):
                self.freeze()
            self.draw_board()
            self.root.after(self.delay, self.update)
        elif self.game_over:
            self.save_score()
            self.canvas.create_text(COLUMNS*CELL_SIZE//2, ROWS*CELL_SIZE//2, text="GAME OVER", fill="red", font=("Helvetica", 24))
            self.pause_button.pack_forget()  # 隐藏暂停按钮
            self.leaderboard_button.pack(side=tk.LEFT)  # 显示排行榜按钮

    def key_press(self, event):
        """处理键盘按键事件"""
        if event.keysym == "Left":
            self.move(0, -1)
        elif event.keysym == "Right":
            self.move(0, 1)
        elif event.keysym == "Down":
            self.move(1, 0)
        elif event.keysym == "Up":
            self.rotate()
        self.draw_board()

    def toggle_pause(self):
        """切换游戏暂停状态"""
        self.paused = not self.paused
        if not self.paused:
            self.update()

    def save_score(self):
        """保存当前分数到文件"""
        with open(SCORE_FILE, "a") as file:
            file.write(f"{self.score}\n")
        if self.score > self.high_score:
            self.high_score = self.score

    def get_high_score(self):
        """获取最高分"""
        if os.path.exists(SCORE_FILE):
            with open(SCORE_FILE, "r") as file:
                scores = [int(line.strip()) for line in file.readlines()]
            if scores:
                return max(scores)
        return 0

    def get_leaderboard_text(self):
        """获取排行榜文本"""
        if os.path.exists(SCORE_FILE):
            with open(SCORE_FILE, "r") as file:
                scores = [int(line.strip()) for line in file.readlines()]
            scores.sort(reverse=True)
            top_scores = scores[:10]
            leaderboard = "\n".join([f"{i+1}. {score}" for i, score in enumerate(top_scores)])
        else:
            leaderboard = "No scores available."
        return leaderboard

    def show_leaderboard(self):
        """显示排行榜窗口"""
        leaderboard_window = tk.Toplevel(self.root)
        leaderboard_window.title("Leaderboard")
        leaderboard_text = self.get_leaderboard_text()
        tk.Label(leaderboard_window, text=leaderboard_text).pack()

    def restart_game(self):
        """重启游戏"""
        self.score = 0
        self.game_over = False
        self.paused = False
        self.board = [[0] * COLUMNS for _ in range(ROWS)]
        self.current_shape = self.new_shape()
        self.current_position = [0, COLUMNS // 2 - len(self.current_shape[0]) // 2]
        self.delay = INITIAL_DELAY  # 重置延迟时间

        # 隐藏排行榜按钮
        self.leaderboard_button.pack_forget()
        
        # 重新布局按钮,确保它们在同一行
        self.button_frame.pack_forget()
        self.button_frame.pack(side=tk.TOP, pady=10)
        self.restart_button.pack(side=tk.LEFT, padx=0)
        self.pause_button.pack(side=tk.LEFT, padx=0)
        
        # 确保按钮框架在网格之前被布局
        self.canvas.pack_forget()
        
        # 更新分数标签并放置在按钮和网格之间
        self.score_label.pack_forget()
        self.score_label.config(text=f"Score: {self.score}  High Score: {self.high_score}")
        self.score_label.pack()
        
        self.canvas.pack()

        self.update()

        self.score_label.config(text=f"Score: {self.score}  High Score: {self.high_score}")
        self.update()

root = tk.Tk()
root.title("Tetris")
root.geometry(f"{COLUMNS*CELL_SIZE+20}x{ROWS*CELL_SIZE+100}+{(root.winfo_screenwidth()//2)-(COLUMNS*CELL_SIZE//2)}+{(root.winfo_screenheight()//2)-(ROWS*CELL_SIZE//2)}")
game = Tetris(root)
root.mainloop()

页面效果


相关推荐
PLM小助手8 分钟前
鼎捷新一代PLM 荣膺维科杯 “2023年度行业优秀产品奖”
java·大数据·前端·人工智能·分布式·低代码·微服务
F-1259 分钟前
关于 vue/cli 脚手架实现项目编译运行的源码解析
前端·javascript·vue.js
骨子里的偏爱12 分钟前
uniapp与webview直接进行传值
前端·chrome·uni-app
闲人编程16 分钟前
Python实现优化的分水岭算法
python·算法·计算机视觉·优化·分水岭
湫兮之风20 分钟前
C++:opencv计算轮廓周长--cv::arcLength
开发语言·c++·opencv
白帽黑客cst29 分钟前
网络安全(黑客技术)2024年—全新自学手册
网络·python·学习·安全·web安全·网络安全
A_cot30 分钟前
HTML5全面知识点
前端·前端框架·html·html5
ya888g31 分钟前
C++ STL 数据结构 vector基本用法
开发语言·数据结构·c++
猛男敲代码31 分钟前
node强缓存
前端
何大春1 小时前
Probabilistic Embeddings for Cross-Modal Retrieval 论文阅读
论文阅读·人工智能·python·深度学习·论文笔记