Skip to content

Instantly share code, notes, and snippets.

@billygl
Created February 4, 2025 02:44
Show Gist options
  • Save billygl/17ff477bc8205b9103617443a04515d9 to your computer and use it in GitHub Desktop.
Save billygl/17ff477bc8205b9103617443a04515d9 to your computer and use it in GitHub Desktop.
tetris python by o3-mini
import pygame
import random
# Global Variables
CELL_SIZE = 30
COLS = 10
ROWS = 20
WIDTH = CELL_SIZE * COLS
HEIGHT = CELL_SIZE * ROWS
# Define colors (RGB)
colors = [
(0, 0, 0), # 0 = empty
(0, 255, 255), # I piece
(0, 0, 255), # J piece
(255, 127, 0), # L piece
(255, 255, 0), # O piece
(0, 255, 0), # S piece
(128, 0, 128), # T piece
(255, 0, 0) # Z piece
]
# Define the shapes and their rotations as 5x5 grids
S = [
['.....',
'.....',
'..00.',
'.00..',
'.....'],
['.....',
'..0..',
'..00.',
'...0.',
'.....']
]
Z = [
['.....',
'.....',
'.00..',
'..00.',
'.....'],
['.....',
'..0..',
'.00..',
'.0...',
'.....']
]
I = [
['..0..',
'..0..',
'..0..',
'..0..',
'.....'],
['.....',
'0000.',
'.....',
'.....',
'.....']
]
O = [
['.....',
'.....',
'.00..',
'.00..',
'.....']
]
J = [
['.....',
'.0...',
'.000.',
'.....',
'.....'],
['.....',
'..00.',
'..0..',
'..0..',
'.....'],
['.....',
'.....',
'.000.',
'...0.',
'.....'],
['.....',
'..0..',
'..0..',
'.00..',
'.....']
]
L = [
['.....',
'...0.',
'.000.',
'.....',
'.....'],
['.....',
'..0..',
'..0..',
'..00.',
'.....'],
['.....',
'.....',
'.000.',
'.0...',
'.....'],
['.....',
'.00..',
'..0..',
'..0..',
'.....']
]
T = [
['.....',
'..0..',
'.000.',
'.....',
'.....'],
['.....',
'..0..',
'..00.',
'..0..',
'.....'],
['.....',
'.....',
'.000.',
'..0..',
'.....'],
['.....',
'..0..',
'.00..',
'..0..',
'.....']
]
shapes = [S, Z, I, O, J, L, T]
shape_colors = [colors[1], colors[2], colors[3], colors[4], colors[5], colors[6], colors[7]]
class Piece:
def __init__(self, x, y, shape):
self.x = x # grid x position
self.y = y # grid y position
self.shape = shape
self.color = shape_colors[shapes.index(shape)]
self.rotation = 0 # current rotation state
def create_grid(locked_positions={}):
grid = [[(0, 0, 0) for _ in range(COLS)] for _ in range(ROWS)]
for r in range(ROWS):
for c in range(COLS):
if (c, r) in locked_positions:
grid[r][c] = locked_positions[(c, r)]
return grid
def convert_shape_format(piece):
"""Convert the 5x5 shape format into grid positions."""
positions = []
format = piece.shape[piece.rotation % len(piece.shape)]
for i, line in enumerate(format):
row = list(line)
for j, column in enumerate(row):
if column == '0':
# Adjust for the grid offset inside the 5x5 representation
positions.append((piece.x + j - 2, piece.y + i - 4))
return positions
def valid_space(piece, grid):
accepted_positions = [[(j, i) for j in range(COLS) if grid[i][j] == (0, 0, 0)] for i in range(ROWS)]
accepted_positions = [pos for sub in accepted_positions for pos in sub]
formatted = convert_shape_format(piece)
for pos in formatted:
if pos not in accepted_positions:
if pos[1] > -1:
return False
return True
def check_lost(locked_positions):
for pos in locked_positions:
x, y = pos
if y < 1:
return True
return False
def get_shape():
return Piece(COLS // 2 - 2, 0, random.choice(shapes))
def draw_grid(surface, grid):
for i in range(ROWS):
# Horizontal lines
pygame.draw.line(surface, (128, 128, 128), (0, i * CELL_SIZE), (WIDTH, i * CELL_SIZE))
for j in range(COLS):
# Vertical lines
pygame.draw.line(surface, (128, 128, 128), (j * CELL_SIZE, 0), (j * CELL_SIZE, HEIGHT))
def clear_rows(grid, locked):
"""Check and clear full rows. Returns the number of rows cleared."""
inc = 0
for i in range(len(grid) - 1, -1, -1):
row = grid[i]
if (0, 0, 0) not in row:
inc += 1
ind = i
for j in range(COLS):
try:
del locked[(j, i)]
except:
continue
if inc > 0:
# Shift rows above cleared row down
for key in sorted(list(locked), key=lambda x: x[1])[::-1]:
x, y = key
if y < ind:
newKey = (x, y + inc)
locked[newKey] = locked.pop(key)
return inc
def draw_window(surface, grid, score=0):
surface.fill((0, 0, 0))
# Title
font = pygame.font.SysFont('comicsans', 40)
label = font.render('Tetris', 1, (255, 255, 255))
surface.blit(label, (WIDTH / 2 - label.get_width() / 2, 10))
# Score
font_small = pygame.font.SysFont('comicsans', 20)
score_label = font_small.render('Score: ' + str(score), 1, (255, 255, 255))
surface.blit(score_label, (WIDTH - score_label.get_width() - 10, HEIGHT / 2))
# Draw the grid blocks
for i in range(ROWS):
for j in range(COLS):
pygame.draw.rect(surface, grid[i][j], (j * CELL_SIZE, i * CELL_SIZE, CELL_SIZE, CELL_SIZE), 0)
draw_grid(surface, grid)
# Border around the grid
pygame.draw.rect(surface, (255, 0, 0), (0, 0, WIDTH, HEIGHT), 5)
def main(win):
locked_positions = {}
grid = create_grid(locked_positions)
change_piece = False
current_piece = get_shape()
next_piece = get_shape()
clock = pygame.time.Clock()
fall_time = 0
fall_speed = 0.5 # seconds per fall step
score = 0
running = True
while running:
grid = create_grid(locked_positions)
fall_time += clock.get_rawtime()
clock.tick()
# Handle falling
if fall_time / 1000 > fall_speed:
fall_time = 0
current_piece.y += 1
if not valid_space(current_piece, grid) and current_piece.y > 0:
current_piece.y -= 1
change_piece = True
# Event handling
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
pygame.display.quit()
quit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
current_piece.x -= 1
if not valid_space(current_piece, grid):
current_piece.x += 1
if event.key == pygame.K_RIGHT:
current_piece.x += 1
if not valid_space(current_piece, grid):
current_piece.x -= 1
if event.key == pygame.K_DOWN:
current_piece.y += 1
if not valid_space(current_piece, grid):
current_piece.y -= 1
if event.key == pygame.K_UP:
current_piece.rotation = (current_piece.rotation + 1) % len(current_piece.shape)
if not valid_space(current_piece, grid):
current_piece.rotation = (current_piece.rotation - 1) % len(current_piece.shape)
shape_pos = convert_shape_format(current_piece)
# Draw current piece onto grid
for pos in shape_pos:
x, y = pos
if y > -1:
grid[y][x] = current_piece.color
# If the piece hit the ground, lock it and generate a new piece
if change_piece:
for pos in shape_pos:
locked_positions[(pos[0], pos[1])] = current_piece.color
current_piece = next_piece
next_piece = get_shape()
change_piece = False
score += clear_rows(grid, locked_positions) * 10
draw_window(win, grid, score)
pygame.display.update()
if check_lost(locked_positions):
font = pygame.font.SysFont('comicsans', 60)
label = font.render("Game Over", 1, (255, 255, 255))
win.blit(label, (WIDTH / 2 - label.get_width() / 2, HEIGHT / 2 - label.get_height() / 2))
pygame.display.update()
pygame.time.delay(2000)
running = False
def main_menu(win):
run = True
while run:
win.fill((0, 0, 0))
font = pygame.font.SysFont('comicsans', 60)
label = font.render("Press any key to start", 1, (255, 255, 255))
win.blit(label, (WIDTH / 2 - label.get_width() / 2, HEIGHT / 2 - label.get_height() / 2))
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
main(win)
pygame.quit()
if __name__ == '__main__':
pygame.init()
win = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption('Tetris')
main_menu(win)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment