-
-
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"<[email protected]> | |
# | |
# 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() |
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
@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?