-
-
Save silvasur/565419 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3 | |
#-*- coding: utf-8 -*- | |
# Very simple tetris implementation | |
# | |
# Control keys: | |
# Down - Drop stone faster | |
# Left/Right - Move stone | |
# Up - Rotate Stone clockwise | |
# Escape - Quit game | |
# P - Pause game | |
# Return - Instant drop | |
# | |
# Have fun! | |
# NOTE: If you're looking for the old python2 version, see | |
# <https://gist.github.com/silvasur/565419/45a3ded61b993d1dd195a8a8688e7dc196b08de8> | |
# Copyright (c) 2010 "Laria Carolin Chabowski"<me@laria.me> | |
# | |
# Permission is hereby granted, free of charge, to any person obtaining a copy | |
# of this software and associated documentation files (the "Software"), to deal | |
# in the Software without restriction, including without limitation the rights | |
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
# copies of the Software, and to permit persons to whom the Software is | |
# furnished to do so, subject to the following conditions: | |
# | |
# The above copyright notice and this permission notice shall be included in | |
# all copies or substantial portions of the Software. | |
# | |
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
# THE SOFTWARE. | |
from random import randrange as rand | |
import pygame, sys | |
# The configuration | |
cell_size = 18 | |
cols = 10 | |
rows = 22 | |
maxfps = 30 | |
colors = [ | |
(0, 0, 0 ), | |
(255, 85, 85), | |
(100, 200, 115), | |
(120, 108, 245), | |
(255, 140, 50 ), | |
(50, 120, 52 ), | |
(146, 202, 73 ), | |
(150, 161, 218 ), | |
(35, 35, 35) # Helper color for background grid | |
] | |
# Define the shapes of the single parts | |
tetris_shapes = [ | |
[[1, 1, 1], | |
[0, 1, 0]], | |
[[0, 2, 2], | |
[2, 2, 0]], | |
[[3, 3, 0], | |
[0, 3, 3]], | |
[[4, 0, 0], | |
[4, 4, 4]], | |
[[0, 0, 5], | |
[5, 5, 5]], | |
[[6, 6, 6, 6]], | |
[[7, 7], | |
[7, 7]] | |
] | |
def rotate_clockwise(shape): | |
return [ | |
[ shape[y][x] for y in range(len(shape)) ] | |
for x in range(len(shape[0]) - 1, -1, -1) | |
] | |
def check_collision(board, shape, offset): | |
off_x, off_y = offset | |
for cy, row in enumerate(shape): | |
for cx, cell in enumerate(row): | |
try: | |
if cell and board[ cy + off_y ][ cx + off_x ]: | |
return True | |
except IndexError: | |
return True | |
return False | |
def remove_row(board, row): | |
del board[row] | |
return [[0 for i in range(cols)]] + board | |
def join_matrixes(mat1, mat2, mat2_off): | |
off_x, off_y = mat2_off | |
for cy, row in enumerate(mat2): | |
for cx, val in enumerate(row): | |
mat1[cy+off_y-1 ][cx+off_x] += val | |
return mat1 | |
def new_board(): | |
board = [ | |
[ 0 for x in range(cols) ] | |
for y in range(rows) | |
] | |
board += [[ 1 for x in range(cols)]] | |
return board | |
class TetrisApp(object): | |
def __init__(self): | |
pygame.init() | |
pygame.key.set_repeat(250,25) | |
self.width = cell_size*(cols+6) | |
self.height = cell_size*rows | |
self.rlim = cell_size*cols | |
self.bground_grid = [[ 8 if x%2==y%2 else 0 for x in range(cols)] for y in range(rows)] | |
self.default_font = pygame.font.Font( | |
pygame.font.get_default_font(), 12) | |
self.screen = pygame.display.set_mode((self.width, self.height)) | |
pygame.event.set_blocked(pygame.MOUSEMOTION) # We do not need | |
# mouse movement | |
# events, so we | |
# block them. | |
self.next_stone = tetris_shapes[rand(len(tetris_shapes))] | |
self.init_game() | |
def new_stone(self): | |
self.stone = self.next_stone[:] | |
self.next_stone = tetris_shapes[rand(len(tetris_shapes))] | |
self.stone_x = int(cols / 2 - len(self.stone[0])/2) | |
self.stone_y = 0 | |
if check_collision(self.board, | |
self.stone, | |
(self.stone_x, self.stone_y)): | |
self.gameover = True | |
def init_game(self): | |
self.board = new_board() | |
self.new_stone() | |
self.level = 1 | |
self.score = 0 | |
self.lines = 0 | |
pygame.time.set_timer(pygame.USEREVENT+1, 1000) | |
def disp_msg(self, msg, topleft): | |
x,y = topleft | |
for line in msg.splitlines(): | |
self.screen.blit( | |
self.default_font.render( | |
line, | |
False, | |
(255,255,255), | |
(0,0,0)), | |
(x,y)) | |
y+=14 | |
def center_msg(self, msg): | |
for i, line in enumerate(msg.splitlines()): | |
msg_image = self.default_font.render(line, False, | |
(255,255,255), (0,0,0)) | |
msgim_center_x, msgim_center_y = msg_image.get_size() | |
msgim_center_x //= 2 | |
msgim_center_y //= 2 | |
self.screen.blit(msg_image, ( | |
self.width // 2-msgim_center_x, | |
self.height // 2-msgim_center_y+i*22)) | |
def draw_matrix(self, matrix, offset): | |
off_x, off_y = offset | |
for y, row in enumerate(matrix): | |
for x, val in enumerate(row): | |
if val: | |
pygame.draw.rect( | |
self.screen, | |
colors[val], | |
pygame.Rect( | |
(off_x+x) * | |
cell_size, | |
(off_y+y) * | |
cell_size, | |
cell_size, | |
cell_size),0) | |
def add_cl_lines(self, n): | |
linescores = [0, 40, 100, 300, 1200] | |
self.lines += n | |
self.score += linescores[n] * self.level | |
if self.lines >= self.level*6: | |
self.level += 1 | |
newdelay = 1000-50*(self.level-1) | |
newdelay = 100 if newdelay < 100 else newdelay | |
pygame.time.set_timer(pygame.USEREVENT+1, newdelay) | |
def move(self, delta_x): | |
if not self.gameover and not self.paused: | |
new_x = self.stone_x + delta_x | |
if new_x < 0: | |
new_x = 0 | |
if new_x > cols - len(self.stone[0]): | |
new_x = cols - len(self.stone[0]) | |
if not check_collision(self.board, | |
self.stone, | |
(new_x, self.stone_y)): | |
self.stone_x = new_x | |
def quit(self): | |
self.center_msg("Exiting...") | |
pygame.display.update() | |
sys.exit() | |
def drop(self, manual): | |
if not self.gameover and not self.paused: | |
self.score += 1 if manual else 0 | |
self.stone_y += 1 | |
if check_collision(self.board, | |
self.stone, | |
(self.stone_x, self.stone_y)): | |
self.board = join_matrixes( | |
self.board, | |
self.stone, | |
(self.stone_x, self.stone_y)) | |
self.new_stone() | |
cleared_rows = 0 | |
while True: | |
for i, row in enumerate(self.board[:-1]): | |
if 0 not in row: | |
self.board = remove_row( | |
self.board, i) | |
cleared_rows += 1 | |
break | |
else: | |
break | |
self.add_cl_lines(cleared_rows) | |
return True | |
return False | |
def insta_drop(self): | |
if not self.gameover and not self.paused: | |
while(not self.drop(True)): | |
pass | |
def rotate_stone(self): | |
if not self.gameover and not self.paused: | |
new_stone = rotate_clockwise(self.stone) | |
if not check_collision(self.board, | |
new_stone, | |
(self.stone_x, self.stone_y)): | |
self.stone = new_stone | |
def toggle_pause(self): | |
self.paused = not self.paused | |
def start_game(self): | |
if self.gameover: | |
self.init_game() | |
self.gameover = False | |
def run(self): | |
key_actions = { | |
'ESCAPE': self.quit, | |
'LEFT': lambda:self.move(-1), | |
'RIGHT': lambda:self.move(+1), | |
'DOWN': lambda:self.drop(True), | |
'UP': self.rotate_stone, | |
'p': self.toggle_pause, | |
'SPACE': self.start_game, | |
'RETURN': self.insta_drop | |
} | |
self.gameover = False | |
self.paused = False | |
dont_burn_my_cpu = pygame.time.Clock() | |
while 1: | |
self.screen.fill((0,0,0)) | |
if self.gameover: | |
self.center_msg("""Game Over!\nYour score: %d | |
Press space to continue""" % self.score) | |
else: | |
if self.paused: | |
self.center_msg("Paused") | |
else: | |
pygame.draw.line(self.screen, | |
(255,255,255), | |
(self.rlim+1, 0), | |
(self.rlim+1, self.height-1)) | |
self.disp_msg("Next:", ( | |
self.rlim+cell_size, | |
2)) | |
self.disp_msg("Score: %d\n\nLevel: %d\ | |
\nLines: %d" % (self.score, self.level, self.lines), | |
(self.rlim+cell_size, cell_size*5)) | |
self.draw_matrix(self.bground_grid, (0,0)) | |
self.draw_matrix(self.board, (0,0)) | |
self.draw_matrix(self.stone, | |
(self.stone_x, self.stone_y)) | |
self.draw_matrix(self.next_stone, | |
(cols+1,2)) | |
pygame.display.update() | |
for event in pygame.event.get(): | |
if event.type == pygame.USEREVENT+1: | |
self.drop(False) | |
elif event.type == pygame.QUIT: | |
self.quit() | |
elif event.type == pygame.KEYDOWN: | |
for key in key_actions: | |
if event.key == eval("pygame.K_" | |
+key): | |
key_actions[key]() | |
dont_burn_my_cpu.tick(maxfps) | |
if __name__ == '__main__': | |
App = TetrisApp() | |
App.run() |
It's a good code. Thanks 👍
How can I make '.py' to '.exe' file?
Of course I can download through the URL you wrote up there.
Just wonder.
error Traceback (most recent call last)
in ()
155
156 if name == 'main':
--> 157 App = TetrisApp()
158 App.run()
in init(self)
2 def init(self):
3 pygame.init()
----> 4 pygame.key.set_repeat(250,25)
5 self.width = config['cell_size']*config['cols']
6 self.height = config['cell_size']*config['rows']
error: video system not initialized
If you help video gives error I would appreciate
@silvasur hi how could i use triangles as the shape for the tetris pieces instead of using squares? like in this example
Thanks! Helped me get started with my Arcade implementation:
https://github.com/pvcraven/arcade/blob/master/arcade/examples/tetris.py
Hey, Why is my program crash because of pygame?
@Error68 Not exactly sure, but you might need to change either your loop or check your IDE for any errors. I highly reccomend you using Pycharm when coding. It gives you a good overlook on whats wrong with your code. Especially if its crashing. Thanks.
if you cant execute the code try type "range" insted of "xrange"
It's a good code. Thanks +1
How can I make '.py' to '.exe' file?
Of course I can download through the URL you wrote up there.
Just wonder.
To be honest, I'm not completely sure, it's been a while... But IIRC, I used py2exe, probably with a similar configuration I used in this project, see mkexe.py and exefying.bat
Hey, Why is my program crash because of pygame?
Do you have pygame for python2 installed? Try starting a python2 interpreter and type "import pygame". If you get an error there, you need to install pygame on your system first.
This is an excellent implementation of Tetris! However, I also got errors for the "xrange" and ended up having to change all of them to just "range". This was for Python 3.5<. Well done otherwise!
This is an excellent implementation of Tetris! However, I also got errors for the "xrange" and ended up having to change all of them to just "range". This was for Python 3.5<. Well done otherwise!
Yes, it's for python2, as seen in the hashbang line (#!/usr/bin/env python2
) at the top.
I should probably rewrite it for python3 some time, also improve / simplify the code, while I'm at it. There are some things I would do different now (as it should be, I was still in school back then, a lot of time has passed).
import pygame, sys
ModuleNotFoundError: No module named 'pygame'
The above is the error message i got.
Hey, Why is my program crash because of pygame?
Do you have pygame for python2 installed? Try starting a python2 interpreter and type "import pygame". If you get an error there, you need to install pygame on your system first.
I may have the same requirement shown above heh?
Do you have pygame for python2 installed? Try starting a python2 interpreter and type "import pygame". If you get an error there, you need to install pygame on your system first.
I may have the same requirement shown above heh?
Yes, the error message tells you that python can't find the pygame library. So I would try to install it and then try again.
Thanks for sharing
I finally converted the game to Python 3 🎉
don't burn my cpu
.
.
.
.
okay
Funny variable names is noice
Hi. Can You give me the code of AI tetris
can somone tell me why i get tons of error messagees and can`t run the game .? what did i forget to istall on visual studio ?
@Dilovan200 Without seeing the error messages, nobody will be able to help you. Can you copy the error messages here or post a screenshot? Maybe then someone might be able to help you. Also not sure, what this has to do with visual studio?
is it possible to make that .py file to desktop app??
@Wax30d I mean, it's an application that runs on a desktop, so it kinda is a desktop app, right? 😃
If you want to bundle the whole application up, so users don't have to install python and pygame, here are some possible options: On Linux you might try to make it a Flatpak app, for Windows there is py2exe. No idea about macOS or other operating systems.
import pygame
import random
colors = [
(0, 0, 0),
(120, 37, 179),
(100, 179, 179),
(80, 34, 22),
(80, 134, 22),
(180, 34, 22),
(180, 34, 122),
]
class Figure:
x = 0
y = 0
figures = [
[[1, 5, 9, 13], [4, 5, 6, 7]],
[[4, 5, 9, 10], [2, 6, 5, 9]],
[[6, 7, 9, 10], [1, 5, 6, 10]],
[[1, 2, 5, 9], [0, 4, 5, 6], [1, 5, 9, 8], [4, 5, 6, 10]],
[[1, 2, 6, 10], [5, 6, 7, 9], [2, 6, 10, 11], [3, 5, 6, 7]],
[[1, 4, 5, 6], [1, 4, 5, 9], [4, 5, 6, 9], [1, 5, 6, 9]],
[[1, 2, 5, 6]],
]
def __init__(self, x, y):
self.x = x
self.y = y
self.type = random.randint(0, len(self.figures) - 1)
self.color = random.randint(1, len(colors) - 1)
self.rotation = 0
def image(self):
return self.figures[self.type][self.rotation]
def rotate(self):
self.rotation = (self.rotation + 1) % len(self.figures[self.type])
class Tetris:
level = 2
score = 0
state = "start"
field = []
height = 0
width = 0
x = 100
y = 60
zoom = 20
figure = None
def __init__(self, height, width):
self.height = height
self.width = width
self.field = []
self.score = 0
self.state = "start"
for i in range(height):
new_line = []
for j in range(width):
new_line.append(0)
self.field.append(new_line)
def new_figure(self):
self.figure = Figure(3, 0)
def intersects(self):
intersection = False
for i in range(4):
for j in range(4):
if i * 4 + j in self.figure.image():
if i + self.figure.y > self.height - 1 or \
j + self.figure.x > self.width - 1 or \
j + self.figure.x < 0 or \
self.field[i + self.figure.y][j + self.figure.x] > 0:
intersection = True
return intersection
def break_lines(self):
lines = 0
for i in range(1, self.height):
zeros = 0
for j in range(self.width):
if self.field[i][j] == 0:
zeros += 1
if zeros == 0:
lines += 1
for i1 in range(i, 1, -1):
for j in range(self.width):
self.field[i1][j] = self.field[i1 - 1][j]
self.score += lines ** 2
def go_space(self):
while not self.intersects():
self.figure.y += 1
self.figure.y -= 1
self.freeze()
def go_down(self):
self.figure.y += 1
if self.intersects():
self.figure.y -= 1
self.freeze()
def freeze(self):
for i in range(4):
for j in range(4):
if i * 4 + j in self.figure.image():
self.field[i + self.figure.y][j + self.figure.x] = self.figure.color
self.break_lines()
self.new_figure()
if self.intersects():
self.state = "gameover"
def go_side(self, dx):
old_x = self.figure.x
self.figure.x += dx
if self.intersects():
self.figure.x = old_x
def rotate(self):
old_rotation = self.figure.rotation
self.figure.rotate()
if self.intersects():
self.figure.rotation = old_rotation
Initialize the game engine
pygame.init()
Define some colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GRAY = (128, 128, 128)
size = (400, 500)
screen = pygame.display.set_mode(size)
pygame.display.set_caption("Tetris")
Loop until the user clicks the close button.
done = False
clock = pygame.time.Clock()
fps = 25
game = Tetris(20, 10)
counter = 0
pressing_down = False
while not done:
if game.figure is None:
game.new_figure()
counter += 1
if counter > 100000:
counter = 0
if counter % (fps // game.level // 2) == 0 or pressing_down:
if game.state == "start":
game.go_down()
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
game.rotate()
if event.key == pygame.K_DOWN:
pressing_down = True
if event.key == pygame.K_LEFT:
game.go_side(-1)
if event.key == pygame.K_RIGHT:
game.go_side(1)
if event.key == pygame.K_SPACE:
game.go_space()
if event.key == pygame.K_ESCAPE:
game.__init__(20, 10)
if event.type == pygame.KEYUP:
if event.key == pygame.K_DOWN:
pressing_down = False
screen.fill(WHITE)
for i in range(game.height):
for j in range(game.width):
pygame.draw.rect(screen, GRAY, [game.x + game.zoom * j, game.y + game.zoom * i, game.zoom, game.zoom], 1)
if game.field[i][j] > 0:
pygame.draw.rect(screen, colors[game.field[i][j]],
[game.x + game.zoom * j + 1, game.y + game.zoom * i + 1, game.zoom - 2, game.zoom - 1])
if game.figure is not None:
for i in range(4):
for j in range(4):
p = i * 4 + j
if p in game.figure.image():
pygame.draw.rect(screen, colors[game.figure.color],
[game.x + game.zoom * (j + game.figure.x) + 1,
game.y + game.zoom * (i + game.figure.y) + 1,
game.zoom - 2, game.zoom - 2])
font = pygame.font.SysFont('Calibri', 25, True, False)
font1 = pygame.font.SysFont('Calibri', 65, True, False)
text = font.render("Score: " + str(game.score), True, BLACK)
text_game_over = font1.render("Game Over", True, (255, 125, 0))
text_game_over1 = font1.render("Press ESC", True, (255, 215, 0))
screen.blit(text, [0, 0])
if game.state == "gameover":
screen.blit(text_game_over, [20, 200])
screen.blit(text_game_over1, [25, 265])
pygame.display.flip()
clock.tick(fps)
pygame.quit()
Hey if you like my code, you can also visit my repo
@FlyingDutchman42: Just removing the
self.draw_matrix(self.next_stone, (cols+1,2))
in the game drawing logic should do the trick.