Skip to content

Instantly share code, notes, and snippets.

@icarob-eng
Last active October 12, 2021 02:47
Show Gist options
  • Save icarob-eng/ed778a745e757938688f182131857090 to your computer and use it in GitHub Desktop.
Save icarob-eng/ed778a745e757938688f182131857090 to your computer and use it in GitHub Desktop.
Um pequeno projeto de Tetris em terminal baseado no jogo orginal (ainda em andamento)
import time
import os
from random import randint
from pynput.keyboard import Key, Listener
import json
# bordas esquerda e direta da tela
ESQ = '<!'
DIR = '!>'
# quadrados vazios ou cheios
CLR = ' .'
BLK = '[]'
# título
TITLE = '*' + '-'*8 + 'TETRIS' + '-'*8 + '*'
# borda inferior da tela
BASE = ESQ + '==' * 10 + DIR + '\n ' + '\\/' * 10
# dicionário com textos laterais ao jogo {linha: conteúdo}
SIDE_TEXTS = {0: ' '*5 + 'Pressione `esc` para sair',
1: ' '*5 + 'Blocos inseridos: ',
2: ' '*5 + 'Linhas completadas: ',
3: ' '*5 + 'Tempo de jogo: '}
# mapa de estados de cada quadrado
MAP_WIDTH = 10
MAP_HEIGHT = 20
state_map = [[False for _ in range(MAP_WIDTH)] for _ in range(MAP_HEIGHT)]
# variáveis de estado
cont = True # variável de continuar o loop
new = False # variável de criar novo bloco
blocos = 0
linhas = 0
tempo = 0
# posição do bloco na mão
block = [0, 3]
H = 4 # altura do bloco
W = 4 # largura do bloco
# estão definidos com 4 como padrão, pois ainda precisaria do mapa de rotação do bloco
# mapas dos blocos:
shapes = {
0: [[1, 1, 1, 1],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]],
1: [[1, 1, 1, 1],
[1, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]],
2: [[1, 1, 0, 0],
[1, 1, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]],
3: [[1, 1, 1, 0],
[0, 1, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]],
4: [[1, 1, 0, 0],
[0, 1, 1, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]]
}
# os outros são meras simetrias de 1 ou 4
cshape = [[True for _ in range(W)] for _ in range(H)] # caixa de colisão do bloco
def cls(): os.system('clear') # função simples para limpar a tela
def new_block():
# apaga as linhas completadas e cria um novo bloco no topo do jogo
global cshape, block, new, blocos, linhas, cont
for i, line in enumerate(state_map):
if i == 0 and True in line:
cont = False
return False
# se tiver um quadrado ocupando a primeira linha quando se cria um novo, o jogo acaba
if False not in line:
state_map.pop(i)
state_map.insert(0, [False for _ in range(MAP_WIDTH)])
# se não tiver um quadrado vazio na linha, apaga a linha e põe outra em branco no topo
linhas += 1
block = [0, 3] # reseta a posição
tipo = randint(0, 6)
if tipo < 5:
cshape = shapes[tipo].copy()
elif 5 <= tipo < 7: # estes são os blocos invertidos
tipo = 1 if tipo == 5 else 4
# troca o tipo pelo equivalente simétrico
cshape = shapes[tipo]
for i in range(W):
cshape[i].reverse()
# inverte todas as linhas
new = False
blocos += 1
return True
def blockfiller(filling):
# função para redesenhar o bloco na lista
x = block[0]
y = block[1]
for i in range(H):
for j in range(W):
try:
state_map[i+x][j+y] = (cshape[i][j] and filling) or \
(state_map[i+x][j+y] and not cshape[i][j])
except IndexError:
pass
# se value for falso qualquer valor no mapa de estados vai ser
# falso, caso contrário, só os valores da forma escolhida
def printer():
# imprime um frame na tela com base na lista
global blocos, linhas
cls()
print(TITLE)
for x, line in enumerate(state_map):
print(ESQ, end='')
for sqr in line:
print(BLK if sqr else CLR, end='')
print(DIR, end='')
if x in SIDE_TEXTS:
print(SIDE_TEXTS[x], end='')
if x == 1:
print(blocos)
elif x == 2:
print(linhas)
elif x == 3:
print(tempo)
else:
print('')
print(BASE)
def actualiser(func, arg=None):
# executa uma função sobre o bloco e atualiza ele
blockfiller(False)
func() if arg is None else func(arg)
blockfiller(True)
printer()
def on_press(key):
# key listener
global cont
try:
if key == Key.esc:
cont = False
return False
elif key == Key.right:
actualiser(lateral, 1)
elif key == Key.left:
actualiser(lateral, -1)
elif key == Key.space:
actualiser(rotate)
elif key == Key.down:
actualiser(vertical, 1)
else:
printer()
except Exception:
cont = False
raise Exception
def lateral(value):
# move o bloco lateralmente e
# checa colisões com os blocos pelo chão ou com as paredes e
# e bloqueia se o movimento lateral for causar coisão
x = block[0]
y = block[1]
result = True
for i in range(H):
for j in range(W):
if cshape[i][j]:
if j + value < W:
neighbor = cshape[i][j + value]
else:
neighbor = False
# checa se o quadrado tem vizinhos no próprio bloco
if not neighbor:
if not 0 <= y + j + value < MAP_WIDTH:
result = False # se não estiver no mapa, não permite o movimento
break
elif state_map[x + i][y + j + value]:
result = False
break
if result:
block[1] += value
def vertical(value):
# checa colisões com os blocos pelo chão ou com o chão e
# retorna se o movimento lateral causaria ou não colisão
x = block[0]
y = block[1]
result = True
for i in range(H):
for j in range(W):
if cshape[i][j]: # só checa o movimento se o quadrado estiver preenchido
if i + value < H:
neighbor = cshape[i + value][j]
else:
neighbor = False
# checa se o quadrado tem vizinhos no próprio bloco
if not neighbor:
if not 0 < x + i + value < MAP_HEIGHT:
result = False
break
elif state_map[x + i + value][y + j]:
result = False
break
if result:
block[0] += value
else:
global new
new = True
def rotate():
# rotaciona o bloco no sentido anti-horário
global cshape
if cshape == shapes[2]:
pass # checa se não é spo um quadrado (que não precisa girar)
else:
x = block[0]
y = block[1]
preview = [list(r) for r in zip(*cshape[::-1])] # previzualização de giro
permit = True
for i in range(H):
for j in range(W):
if preview[i][j] and (state_map[i+x][j+y] and not cshape[i][j]):
permit = False # testa se o giro vai colidir com algo
break
elif preview[i][j] and (not 0 <= x+i < MAP_HEIGHT or not 0 <= y+j < MAP_WIDTH):
permit = False # testa se o giro vai fugir da tela
break
if permit:
cshape = preview.copy()
def save():
name = input('~~Fim de jogo!~~\nEntre com seu nome para salvar sua pontuação\n->')
dados = {'Nome': name, 'Linhas completadas': linhas, 'Blocos inseridos': blocos, 'Tempo de jogo': tempo}
with open('tetris.json', 'w+') as f:
try:
lista = list(json.load(f))
except json.decoder.JSONDecodeError:
lista = []
lista.append(dados)
json.dump(lista, f)
def main(step):
global tempo
printer()
new_block()
listener = Listener(on_press=on_press)
listener.start()
try:
while cont:
tempo += 1
actualiser(vertical, 1)
time.sleep(step)
if new:
if not new_block():
listener.stop()
except KeyboardInterrupt:
listener.stop()
finally:
cls()
save()
print('Obrigado por jogar Tetris')
if input('... Gostaria de jogar de novo? (s/n)\n->') == 's':
main(step)
if __name__ == '__main__':
main(1)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment