鱼C论坛

 找回密码
 立即注册
查看: 768|回复: 84

[作品展示] 俄罗斯方块Tetris(tkinter)

[复制链接]
发表于 2025-7-21 12:32:42 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 pyzyd 于 2025-7-21 12:35 编辑


代码实现:
动画3.gif

感觉写了一坨屎山


源代码:

  1. import tkinter as tk
  2. from tkinter import messagebox
  3. import random
  4. import time


  5. class Game(tk.Tk):
  6.     def __init__(self):
  7.         tk.Tk.__init__(self)
  8.         self.title('Tetris')
  9.         # 固定边框
  10.         self.resizable(False,False)
  11.         # 设置边长、像素
  12.         self.width = 12
  13.         self.height = 20
  14.         self.px = 30

  15.         # 设置不同形状的格子
  16.         self.shapes = {
  17.             'O': [(-1, -1), (0, -1), (-1, 0), (0, 0)],
  18.             'I': [(0, 1), (0, 0), (0, -1), (0, -2)],
  19.             'T': [(-1, -1), (0, -1), (0, 0), (1, -1)],
  20.             'Z': [(-1, 0), (0, 0), (0, 1), (1, 1)],
  21.             'S': [(-1, 1), (0, 1), (0, 0), (1, 0)],
  22.             'L': [(1, 1), (0, 1), (0, 0), (0, -1)],
  23.             'J': [(-1, 1), (0, 1), (0, 0), (0, -1)],
  24.         }

  25.         # 设置不同格子的颜色
  26.         self.colors = ['#FF0000', '#FF7F00', "#D4D432", "#00FF00", "#0000FF", '#4B008B', "#D3007F"]

  27.         self.shape_index = random.choice(list(self.shapes.keys()))
  28.         self.shape_color = random.choice(self.colors)
  29.         self.shape = self.shapes[self.shape_index]

  30.         # 表示固定的格子
  31.         self.fixed_shape = {}
  32.         self.fixed_set = set()

  33.         # 创建画布
  34.         self.canvas = tk.Canvas(
  35.             self, width=self.width*self.px,
  36.             height=self.height*self.px)
  37.         self.canvas.grid(row=0,column=0,columnspan=3,rowspan=4, padx=10, pady=10)

  38.         self.btnr = tk.Button(self, text='<', command=lambda:self.move((-1, 0)))
  39.         self.btnr.grid(row=4, column=0, padx=10, pady=10, sticky='nsew')

  40.         self.btn_rotate = tk.Button(self, text='-', command=self.rotate_shape)
  41.         self.btn_rotate.grid(row=4, column=1, padx=10, pady=10, sticky='nsew')

  42.         self.btnd = tk.Button(self, text='v', command=lambda:self.move((0, 1)))
  43.         self.btnd.grid(row=5, column=1, padx=10, pady=10, sticky='nsew')

  44.         self.btnl = tk.Button(self, text='>', command=lambda:self.move((1, 0)))
  45.         self.btnl.grid(row=4, column=2, padx=10, pady=10, sticky='nsew')

  46.         self.score = 0
  47.         self.v = tk.StringVar()
  48.         self.v.set(f'当前分数:{self.score}')

  49.         self.label1 = tk.Label(self, textvariable=self.v,font=('微软雅黑', 24))
  50.         self.label1.grid(row=0, column=3, padx=10, pady=10, sticky='nsew')

  51.         self.best_score = self.read_best_score()
  52.         self.v2 = tk.StringVar()
  53.         self.v2.set(f'最高分:{self.best_score}')
  54.         self.label2 = tk.Label(self, textvariable=self.v2,font=('微软雅黑', 24))
  55.         self.label2.grid(row=1, column=3, padx=10, pady=10, sticky='nsew')

  56.         self.text = tk.Text(self, height=10, width=20, font=('微软雅黑', 18))
  57.         self.text.grid(row=2, column=3, padx=10, pady=10, sticky='nsew')

  58.         self.text.insert(tk.END, '游戏规则:\n')
  59.         self.text.insert(tk.END, '1. 使用方向键控制方块的移动\n')
  60.         self.text.insert(tk.END, '2. 使用方向上键(Up键)旋转方块\n')
  61.         self.text.insert(tk.END, '3. 消除满行即可获得分数\n')
  62.         self.text.insert(tk.END, '4. 超出画布即游戏结束\n')

  63.         self.bind('<Left>', lambda event:self.move((-1, 0)))
  64.         self.bind('<Right>', lambda event:self.move((1, 0)))
  65.         self.bind('<Up>', lambda event:self.rotate_shape())
  66.         self.bind('<Down>', lambda event:self.move((0, 1)))
  67.         
  68.         # 设置画布的点集(范围)
  69.         self.canvas_set = set()
  70.         for i in range(0, self.width):
  71.             for j in range(0, self.height):
  72.                 self.canvas_set.add((i,j))
  73.         
  74.         # 运行游戏
  75.         self.run_game()


  76.     # 读取最佳分数
  77.     def read_best_score(self):
  78.         """读取最高分"""
  79.         try:
  80.             # 打开best_score.txt文件,以只读模式
  81.             with open('best_score.txt', 'r') as f:
  82.                 # 读取文件内容,并转换为整数
  83.                 return int(f.read())
  84.         except:
  85.             # 如果出现异常,返回0
  86.             return 0
  87.         
  88.     # 定义一个方法,用于将最高分写入文件
  89.     def write_best_score(self, score):
  90.         """写入最高分"""
  91.         # 打开文件best_score.txt,以写入模式
  92.         with open('best_score.txt', 'w') as f:
  93.             # 将最高分转换为字符串,并写入文件
  94.             f.write(str(score))

  95.     # 运行游戏
  96.     def run_game(self):
  97.         # 初始化游戏
  98.         self.initialize()
  99.         # 自动下落
  100.         self.auto_down()
  101.         # 进入主循环
  102.         self.mainloop()

  103.     def initialize(self):
  104.         """初始化界面"""
  105.         # 绘制背景
  106.         self.draw_bg()
  107.         # 生成初始格子
  108.         self.generate_shape()
  109.         # 绘制初始
  110.         self.draw_shape(self.shape, self.x, self.y, self.shape_color)

  111.     def generate_shape(self):
  112.         """生成新的格子"""
  113.         self.shape_index = random.choice(list(self.shapes.keys()))
  114.         self.shape_color = random.choice(self.colors)
  115.         self.shape = self.shapes[self.shape_index]
  116.         # 设置初始位置
  117.         self.x = random.randint(self.width//4, self.width//4*3)
  118.         self.y = -4
  119.         

  120.     def auto_down(self):
  121.         """自动下移"""
  122.         # 翻转方块
  123.         self.flip()
  124.         # 移动方块
  125.         self.move()
  126.         # 每隔500毫秒调用一次auto_down方法
  127.         self.after(500, self.auto_down)

  128.    
  129.     def move(self, direction=(0, 1)):
  130.         """移动格子"""
  131.         # 获取移动方向
  132.         dx, dy = direction
  133.         
  134.         # 更新格子的坐标
  135.         self.x += dx
  136.         self.y += dy
  137.         
  138.         # 如果格子超出边界,则回退到原来的位置
  139.         if self.is_out():
  140.             self.x -= dx
  141.             self.y -= dy

  142.         # 如果格子到达底部,则将格子固定在底部,并生成新的格子
  143.         if self.is_bottom():
  144.             # 如果格子颜色已经在固定格子的集合中,则更新该格子的坐标
  145.             if self.shape_color in self.fixed_shape:
  146.                 self.fixed_shape[self.shape_color].update([(self.x+dx,self.y+dy) for dx,dy in self.shape])
  147.             # 如果格子颜色不在固定格子的集合中,则将格子添加到固定格子的集合中
  148.             else:
  149.                 self.fixed_shape[self.shape_color] = set([(self.x+dx,self.y+dy) for dx,dy in self.shape])
  150.             # 将格子的坐标添加到固定格子的集合中
  151.             for x, y in self.shape:
  152.                 self.fixed_set.add((self.x + x, self.y + y))
  153.             # 生成新的格子
  154.             self.generate_shape()
  155.             
  156.    

  157.     def is_over(self):
  158.         """判断游戏是否结束"""
  159.         for x, y in self.fixed_set:
  160.             if y < 0:
  161.                 return True
  162.         return False

  163.     def rotate_shape(self):
  164.         """旋转格子"""
  165.         if self.shape != self.shapes['O']:
  166.             temp = [(-dy, dx) for dx, dy in self.shape]
  167.             for dx, dy in temp:
  168.                 if self.x + dx < 0:
  169.                     self.x += 1
  170.                 elif self.x + dx >= self.width:
  171.                     self.x -= 1
  172.             # 检查旋转后的形状是否与已经固定的方块重叠
  173.             if not self.is_overlap():
  174.                 self.shape = temp


  175.     def is_overlap(self):
  176.         """检查新的格子是否与已经固定的格子重叠"""
  177.         for dx, dy in self.shape:
  178.             if (self.x + dx, self.y + dy) in self.fixed_set:
  179.                 return True
  180.         return False
  181.         

  182.     def is_out(self):
  183.         """判断是否越界"""
  184.         for dx, dy in self.shape:
  185.             if self.x + dx < 0 or self.x + dx >= self.width or self.y + dy >= self.height:
  186.                 return True
  187.         if self.is_overlap():
  188.             return True
  189.         return False
  190.         
  191.     def is_bottom(self):
  192.         """判断是否到底"""
  193.         for dx, dy in self.shape:
  194.             if self.y + dy == self.height - 1:
  195.                 return True
  196.             if (self.x + dx, self.y + dy + 1) in self.fixed_set:
  197.                 return True
  198.         return False
  199.    
  200.     def flip(self):
  201.         """更新画布"""
  202.         # 删除画布上所有元素
  203.         self.canvas.delete("all")
  204.         # 重绘元素
  205.         self.draw_bg()

  206.         y_s = self.is_delete()
  207.         if y_s:
  208.             index_y = y_s[0]
  209.             l = len(y_s)
  210.             self.score += l * 10
  211.             self.v.set(f'当前分数:{self.score}')
  212.             if self.score > self.best_score:
  213.                 self.best_score = self.score
  214.                 self.v2.set(f'最高分:{self.best_score}')
  215.                 self.write_best_score(self.best_score)
  216.             for shape_color, _ in self.fixed_shape.items():
  217.                 temp_list = list(_)
  218.                 for x, y in _:
  219.                     if y < index_y:
  220.                         temp_list.remove((x, y))
  221.                         temp_list.append((x, y + l))
  222.                         self.fixed_set.discard((x, y))
  223.                         self.fixed_set.add((x, y + l))
  224.                 self.fixed_shape[shape_color] = set(temp_list)

  225.         for shape_color, _ in self.fixed_shape.items():
  226.             for x, y in _:
  227.                 self.draw_rect(x, y, shape_color)

  228.         self.draw_shape(self.shape, self.x, self.y, self.shape_color)

  229.         # 判断是否结束
  230.         if self.is_over():
  231.             # 游戏结束
  232.             self.game_over()

  233.         


  234.     def draw_bg(self):
  235.         """"绘制像素"""
  236.         # 设置背景像素颜色
  237.         fill = '#CCCCCC'
  238.         outline = 'white'
  239.         px = self.px
  240.         for x, y in list(self.canvas_set):
  241.             self.draw_rect(x, y, fill, outline)

  242.     def draw_rect(self, x, y, color, outline="white"):
  243.         """"绘制像素"""
  244.         px = self.px
  245.         # 绘制像素
  246.         self.canvas.create_rectangle(
  247.             x * px, y * px,
  248.             (x + 1) * px, (y + 1) * px,
  249.             fill=color, outline=outline
  250.         )

  251.     def draw_shape(self, shape, x, y, color):
  252.         """绘制不同的格子"""
  253.         for dx, dy in shape:
  254.             self.draw_rect(x + dx, y + dy, color)


  255.     def is_delete(self):
  256.         """判断是否可以删除"""
  257.         y_s = []
  258.         for y in range(self.height):
  259.             if all((x, y) in self.fixed_set for x in range(self.width)):
  260.                 for x in range(self.width):
  261.                     self.fixed_set.remove((x, y))
  262.                     for _ in self.fixed_shape.values():
  263.                         _.discard((x, y))
  264.                 y_s.append(y)
  265.         return y_s

  266.     def game_over(self):
  267.         """游戏结束"""
  268.         self.canvas.create_text(
  269.             self.width * self.px / 2,
  270.             self.height * self.px / 2,
  271.             text="Game Over",
  272.             font=("Arial", 36),
  273.             fill="red"
  274.         )
  275.         self.canvas.update()
  276.         time.sleep(1)
  277.         yesno = messagebox.askyesno("提示", "是否重新开始游戏?")
  278.         self.destroy()
  279.         if yesno:
  280.             self.__init__()


  281. if __name__ == '__main__':
  282.     game = Game()
复制代码




打包好的文件:
https://wwrx.lanzoum.com/iAsuh31hj84d
密码:b5pi

评分

参与人数 7荣誉 +29 鱼币 +29 贡献 +27 C币 +5 收起 理由
smallwh + 5 + 5 + 3 鱼C有你更精彩^_^
小甲鱼 + 5 + 5 + 5 + 5 鱼C有你更精彩^_^
小甲鱼的二师兄 + 3 + 3 + 3 无条件支持楼主!
~风介~ + 3 + 3 + 5 鱼C有你更精彩^_^
当下的力量 + 3 + 3 + 3 鱼C有你更精彩^_^
青出于蓝 + 5 + 5 + 5 鱼C有你更精彩^_^
cjjJasonchen + 5 + 5 + 3 狠狠支持!

查看全部评分

本帖被以下淘专辑推荐:

小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2025-7-21 13:22:54 | 显示全部楼层

回帖奖励 +5 鱼币

挺好的
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2025-7-21 13:58:26 | 显示全部楼层

回帖奖励 +5 鱼币

古德
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2025-7-21 14:11:35 | 显示全部楼层

回帖奖励 +5 鱼币

666
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2025-7-21 14:34:02 | 显示全部楼层
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2025-7-21 14:47:08 | 显示全部楼层

回帖奖励 +5 鱼币

小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2025-7-21 15:33:51 | 显示全部楼层

回帖奖励 +5 鱼币

小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2025-7-21 19:50:34 | 显示全部楼层
不错,支持~
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2025-7-21 21:31:41 | 显示全部楼层

回帖奖励 +5 鱼币

小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2025-7-21 21:46:29 | 显示全部楼层
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2025-7-21 23:30:44 | 显示全部楼层

回帖奖励 +5 鱼币

小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2025-7-22 08:28:46 | 显示全部楼层

回帖奖励 +5 鱼币

狠狠赞!
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2025-7-22 10:21:38 | 显示全部楼层

回帖奖励 +5 鱼币

小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2025-7-22 10:32:42 | 显示全部楼层
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2025-7-22 11:02:07 | 显示全部楼层

回帖奖励 +5 鱼币

历害啊
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2025-7-22 12:09:24 | 显示全部楼层
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2025-7-22 12:43:10 | 显示全部楼层

回帖奖励 +5 鱼币

gooddddddd
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2025-7-22 13:45:55 | 显示全部楼层
厉害
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2025-7-22 18:28:31 | 显示全部楼层

回帖奖励 +5 鱼币

支持
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2025-7-26 19:31:18 From FishC Mobile | 显示全部楼层

回帖奖励 +5 鱼币

哈哈一坨屎也喜欢看
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-8-20 14:16

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表