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

news2024/11/14 11:58:35

游戏界面:

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

游戏规则

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

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

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

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

游戏代码

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()

页面效果

在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2082709.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Windows隐藏起你的秘密文件以及文件夹工具

我们都知道&#xff0c;在 Windows 中可以右键文件夹&#xff0c;选择”属性“&#xff0c;勾选”隐藏“来实现隐藏某个文件夹。 我们还知道&#xff0c;在 Windows 中可以选择勾选 ”显示隐藏的项目和文件夹“&#xff0c;来使上述方法变得形同虚设。 本工具就是用于解决以上…

Spring框架:开发者的得力伙伴,魅力何在?

目录 一. Spring介绍 二. Spring搭建 三. Spring Bean管理 ▐ 管理方式 ▐ 依赖注入 四. Spring数据访问层管理 五. Spring集成MyBatis 海漫浩浩,我亦苦作舟!大家一起学习,一起进步! 一. Spring介绍 Spring是什么? Spring 是一个轻量级的, IOC 和 AOP 的一站式 J…

刷机维修进阶教程-----小米系列机型手机端 直接修改参数步骤解析

在前面分享的修改机型参数的博文中都是开启端口。然后使用电脑端工具类 进行修改参数。今天来给大家解析下小米系列机型如何直接在手机端上面进行修改。在这里要切记。操作前需要备份原机的各项基带参数 分区以及基带qcn。避免操作失误无信号无基带故障出现。 通过博文可以初…

产业园的运营到底有多重要?

在当今经济高速发展的时代&#xff0c;产业园如雨后春笋般涌现&#xff0c;成为推动产业升级和区域经济发展的重要力量&#xff0c;然而&#xff0c;一个成功的产业园并非仅仅依靠开发建设就能实现其价值&#xff0c;更需要精心的运营管理。为什么这么说&#xff1f;今天我们就…

RSA加密解密算法认识及signln_resolve

RSA初步了解 一、首先认识rsa是一种非对称加密算法。 对称加密&#xff1a;加密解密使用同一个密钥&#xff0c;加密者接受信息采用密钥加密&#xff0c;而到破译者手中还是使用这份密钥。这种方式的优点是加密效率高、加解密速度快&#xff0c;但是缺点是密钥需要共享&#x…

如何评价快手可灵的图生视频能力,和市场同类产品相比孰优孰劣?

利用AI工具批量生成影视短剧推广https://docs.qq.com/doc/DYnl6d0FLdHp0V2ll 作为可灵AI官方认证为数不多的几十名超级创作者之一&#xff0c;同时也是AI视频生成多种应用的深度使用者&#xff0c;我来回答下这个问题。 我认为快手可灵是目前市场上无可争议&#xff0c;顶级的图…

python方法介绍

python基础语法 前言导读:Python的创始⼈人为吉多范罗苏姆(Guido van Rossum)。 1989年年的圣诞节期间,吉多范罗苏姆为了了在阿姆斯特丹打发时间,决⼼开发一个新的脚本解释程序,作为ABC语言的一种继承。其诞生于1990年; 1. 基础常识 1.1 python的发展 1989年,Guido (…

Python青少年简明教程:流程控制语句

Python青少年简明教程&#xff1a;流程控制语句 流程控制是编程语言中用于控制程序执行顺序的机制。它决定了代码块的执行路径&#xff0c;包括哪些语句会被执行、哪些不会被执行以及在什么条件下执行。 本节介绍Python的流程控制语句。 if语句 if语句简单的语法格式如下&…

通过观测云 eBPF Tracing 实现无埋点的全链路追踪

前言 随着微服务架构的普及和系统复杂度的增加&#xff0c;对应用程序的可观测性要求也越来越高。传统的监控方法通常需要在应用程序中添加代码来记录和追踪重要信息&#xff0c;这种方法可能会增加系统的负担&#xff0c;并且在复杂系统中维护难度较大。 eBPF&#xff08;Ex…

翻译_Clock Domain Crossing Design

翻译_Clock Domain Crossing Design 原文标题及连接:Clock Domain Crossing (CDC) Design & Verification Techniques Using SystemVerilog. 作者:Clifford E. Cummings Sunburst Design, Inc. cliffc@sunburst-design.com 摘要 在多时钟设计中,需要在时钟域交叉(…

SpringBoot——请求响应(简单参数、实体参数、数组集合参数、日期参数、JSON参数、路径参数、统一响应结果)

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全干发展 &#x1f4c3;个人状态&#xff1a; 研发工程师&#xff0c;现效力于中国工业软件事业 &#x1f680;人生格言&#xff1a; 积跬步…

(经验)SVN降版本,保留版本信息和用户信息。

背景&#xff1a;由于开始公司人数规模小&#xff0c;没有关心SVN最新版本免费对于用户数量限制要求不敏感&#xff0c;随着人数越来越多&#xff0c;公司来了新员工已经添加不了SVN需要注册码了&#xff0c;不利于SVN文件管理的在公司内部的推广。看了好多资料&#xff0c;都没…

Java:jdk8之后新增的时间API

文章目录 为什么要使用新增的API新增了哪些&#xff1f;Local常用方法代码一样的用法 黑马学习笔记 使用新增的 为什么要使用新增的API 新增了哪些&#xff1f; Local 常用方法 代码 package NewTime;import java.time.LocalDate;/*** Author: ggdpzhk* CreateTime: 2024-08-…

【Python入门思维概括图】

&#x1f4d6;入门思维概括图 ✅第1节 基础语法 ✅第2节 判断语句 ✅第3节 循环语句 ✅第4节 函数 ✅第5节 数据容器 ✅第6节 函数进阶 ✅第7节 文件操作 ✅第8节 异常模块和包 拓展&#xff1a;学了python能干什么&#xff0c;可以参考下面的Python使用场景思维导图介绍

慈云寺北里社区新时代文明实践站联手鼓动青春丰富青少年暑期生活

炎炎夏日的暑期&#xff0c;慈云寺北里社区新时代文明实践站携手鼓动青春&#xff0c;为社区青少年精心策划并成功举办了两场丰富多彩的暑期活动&#xff0c;不仅为孩子们的假期生活增添了浓厚的文化色彩&#xff0c;更激发了他们对中华传统文化与现代艺术的浓厚兴趣。 首场&am…

《JavaEE进阶》----4.<SpringMVC①简介、基本操作>

本篇博客讲解 MVC思想、及Spring MVC&#xff08;是对MVC思想的一种实现&#xff09;。 Spring MVC的基本操作、学习了六个注解 RestController注解 RequestMappering注解 RequestParam注解 RequestBody注解 PathVariable注解 RequestPart注解 MVC View(视图) 指在应⽤程序中…

IO复用技术

一、I/O复用的介绍 I/O复用是一种能够同时处理多个I/O操作的技术&#xff0c;适用于高并发场景。 使用场景 select/poll&#xff1a;适用于连接数较少的场景。epoll&#xff1a;适用于大规模的网络服务&#xff0c;如Web服务器。 二、实现方式 1.select select 是一种系统调…

iPhone突然黑屏?别慌,这里有你的自救指南

在日常使用iPhone的过程中&#xff0c;不少用户可能会遇到手机突然黑屏的情况&#xff0c;这往往让人措手不及。别担心&#xff0c;今天我们就来详细探讨一下iPhone突然黑屏的可能原因及解决方法&#xff0c;帮助你快速恢复手机的正常使用。 一、iPhone突然黑屏的可能原因 1. …

在做结果集封装时,若结果集表头与成员变量名不一致该如何解决?

目录 1. 通过别名映射为一致&#xff1a;as子句 2. 使用resultMap 3. 在配置文件中开启适配规则 在Score实体类中&#xff0c;我们定义了studentId、courseId、score、status&#xff0c;而在数据库中&#xff0c;我们设置如下&#xff1a; 1. 通过别名映射为一致&#xff1…

GAMES202——作业1 实时阴影(ShadowMap,PCF,PCSS)

目录 任务 ShadowMap PCF PCSS 实现 ShadowMap useShadowMap PCF findBlocker PCSS 结果 任务 ShadowMap 1.在 ShadowMaterial.js 中需要向 Shader 传递正确的 uLightMVP 矩阵&#xff0c;该矩阵参与了第一步从光源处渲染场景从而构造ShadowMap 的过程。 你需要完成 D…