import tkinter as tk
import random
from threading import Lock
COLORS = ['gray', 'lightgreen', 'pink', 'blue', 'orange', 'purple','black','yellow']
class Tetris():
FIELD_HEIGHT = 20
FIELD_WIDTH = 25
SCORE_PER_ELIMINATED_LINES = (0, 40, 100, 300, 1200)
TETROMINOS = [
[(0, 0), (0, 1), (1, 0), (1,1)], # O
[(0, 0), (0, 1), (1, 1), (2,1)], # L
[(0, 1), (1, 1), (2, 1), (2,0)], # J
[(0, 1), (1, 0), (1, 1), (2,0)], # Z
[(0, 1), (1, 0), (1, 1), (2,1)], # T
[(0, 0), (1, 0), (1, 1), (2,1)], # S
[(0, 1), (1, 1), (2, 1), (3,1)], # I
]
def __init__(self):
self.field = [[0 for c in range(Tetris.FIELD_WIDTH)] for r in range(Tetris.FIELD_HEIGHT)]
self.score = 0
self.level = 0
self.total_lines_eliminated = 0
self.game_over = False
self.move_lock = Lock()
self.reset_tetromino()
def reset_tetromino(self):
self.tetromino = random.choice(Tetris.TETROMINOS)[:]
self.tetromino_color = random.randint(1, len(COLORS)-1)
self.tetromino_offset = [-2, Tetris.FIELD_WIDTH//2]
self.game_over = any(not self.is_cell_free(r, c) for (r, c) in self.get_tetromino_coords())
def get_tetromino_coords(self):
return [(r+self.tetromino_offset[0], c + self.tetromino_offset[1]) for (r, c) in self.tetromino]
def apply_tetromino(self):
for (r, c) in self.get_tetromino_coords():
self.field[r][c] = self.tetromino_color
new_field = [row for row in self.field if any(tile == 0 for tile in row)]
lines_eliminated = len(self.field)-len(new_field)
self.total_lines_eliminated += lines_eliminated
self.field = [[0]*Tetris.FIELD_WIDTH for x in range(lines_eliminated)] + new_field
self.score += Tetris.SCORE_PER_ELIMINATED_LINES[lines_eliminated] * (self.level + 1)
self.level = self.total_lines_eliminated // 10
self.reset_tetromino()
def get_color(self, r, c):
return self.tetromino_color if (r, c) in self.get_tetromino_coords() else self.field[r][c]
def is_cell_free(self, r, c):
return r < Tetris.FIELD_HEIGHT and 0 <= c < Tetris.FIELD_WIDTH and (r < 0 or self.field[r][c] == 0)
def move(self, dr, dc):
with self.move_lock:
if self.game_over:
return
if all(self.is_cell_free(r + dr, c + dc) for (r, c) in self.get_tetromino_coords()):
self.tetromino_offset = [self.tetromino_offset[0] + dr, self.tetromino_offset[1] + dc]
elif dr == 1 and dc == 0:
self.game_over = any(r < 0 for (r, c) in self.get_tetromino_coords())
if not self.game_over:
self.apply_tetromino()
def rotate(self):
with self.move_lock:
if self.game_over:
self.__init__()
return
ys = [r for (r, c) in self.tetromino]
xs = [c for (r, c) in self.tetromino]
size = max(max(ys) - min(ys), max(xs)-min(xs))
rotated_tetromino = [(c, size-r) for (r, c) in self.tetromino]
wallkick_offset = self.tetromino_offset[:]
tetromino_coord = [(r+wallkick_offset[0], c + wallkick_offset[1]) for (r, c) in rotated_tetromino]
min_x = min(c for r, c in tetromino_coord)
max_x = max(c for r, c in tetromino_coord)
max_y = max(r for r, c in tetromino_coord)
wallkick_offset[1] -= min(0, min_x)
wallkick_offset[1] += min(0, Tetris.FIELD_WIDTH - (1 + max_x))
wallkick_offset[0] += min(0, Tetris.FIELD_HEIGHT - (1 + max_y))
tetromino_coord = [(r+wallkick_offset[0], c + wallkick_offset[1]) for (r, c) in rotated_tetromino]
if all(self.is_cell_free(r, c) for (r, c) in tetromino_coord):
self.tetromino, self.tetromino_offset = rotated_tetromino, wallkick_offset
class Application(tk.Frame):
def init(self, master=None):
super().init(master)
self.tetris = Tetris()
self.pack()
self.create_widgets()
self.update_clock()
def update_clock(self):
self.tetris.move(1, 0)
self.update()
self.master.after(int(1000*(0.66**self.tetris.level)), self.update_clock)
def create_widgets(self):
PIECE_SIZE = 30
self.canvas = tk.Canvas(self, height=PIECE_SIZE*self.tetris.FIELD_HEIGHT,
width = PIECE_SIZE*self.tetris.FIELD_WIDTH, bg="black", bd=0)
self.canvas.bind('<Left>', lambda _: (self.tetris.move(0, -1), self.update()))
self.canvas.bind('<Right>', lambda _: (self.tetris.move(0, 1), self.update()))
self.canvas.bind('<Down>', lambda _: (self.tetris.move(1, 0), self.update()))
self.canvas.bind('<Up>', lambda _: (self.tetris.rotate(), self.update()))
self.canvas.focus_set()
self.rectangles = [
self.canvas.create_rectangle(c*PIECE_SIZE, r*PIECE_SIZE, (c+1)*PIECE_SIZE, (r+1)*PIECE_SIZE)
for r in range(self.tetris.FIELD_HEIGHT) for c in range(self.tetris.FIELD_WIDTH)
]
self.canvas.pack(side="left")
self.status_msg = tk.Label(self, anchor='w', width=11, font=("Courier", 24))
self.status_msg.pack(side="top")
self.game_over_msg = tk.Label(self, anchor='w', width=11, font=("Courier", 24), fg='red')
self.game_over_msg.pack(side="top")
def update(self):
for i, _id in enumerate(self.rectangles):
color_num = self.tetris.get_color(i//self.tetris.FIELD_WIDTH, i % self.tetris.FIELD_WIDTH)
self.canvas.itemconfig(_id, fill=COLORS[color_num])
self.status_msg['text'] = "Score: {}\nLevel: {}".format(self.tetris.score, self.tetris.level)
self.game_over_msg['text'] = "GAME OVER.\nPress UP\nto reset" if self.tetris.game_over else ""
root = tk.Tk()
app = Application(master=root)
app.mainloop()
在网上找到的代码,自己照着写,先是无法运行,于是就在网上学习安装了pygame,成功运行之后,又尝试修改了其颜色、界面大小和下落速度等参数。