1. Voraussetzungen
- Python 3 installiert (mindestens Version 3.8).
- Pygame Bibliothek:bash
pip install pygame - Ein Editor deiner Wahl (z. B. VS Code, PyCharm oder einfach Notepad++).
2. Projektstruktur
Du brauchst nur eine Datei: tetris.py. Darin kommt der gesamte Code, den ich dir gegeben habe.
3. Aufbau des Codes
Der Code ist modular gegliedert:
- Konstanten & Einstellungen Spielfeldgröße, Farben, Geschwindigkeit, Level‑Logik.
- Tetromino‑Definitionen Alle Formen (I, O, T, S, Z, J, L) als 4×4‑Matrizen.
- Hilfsfunktionen
new_piece()erzeugt einen neuen Stein.get_cells()berechnet die belegten Zellen.valid_position()prüft, ob ein Stein passt.lock_piece()fixiert einen Stein im Grid.clear_lines()entfernt volle Reihen.
- Zeichenfunktionen Zeichnen von Spielfeld, Steinen, Panel (Score/Level).
- Game Loop (
main())- Eingaben verarbeiten (Pfeiltasten, Alt, Strg, Space).
- Spiellogik (fallen lassen, Linien löschen, Level erhöhen).
- Zeichnen und Aktualisieren des Bildschirms.
4. Steuerung
- ← / →: Stein bewegen
- ↓: schneller fallen lassen (Soft Drop)
- Alt: Drehung im Uhrzeigersinn
- Strg: Drehung gegen den Uhrzeigersinn
- Leertaste: Hard Drop (sofort absetzen, Bonuspunkte)
- R: Neustart nach Game Over
5. Level & Punkte
- Jede gelöschte Reihe bringt Punkte:
- 1 Reihe = 100 Punkte
- 2 Reihen = 300 Punkte
- 3 Reihen = 500 Punkte
- 4 Reihen = 800 Punkte
- Nach 10 Reihen steigt das Level.
- Mit jedem Level wird die Fallgeschwindigkeit schneller.
6. Game Over
Wenn ein neuer Stein nicht mehr ins Spielfeld passt → Game Over. Dann erscheint die Meldung und du kannst mit R neu starten.
7. Erweiterungen (für dich als kreativer Builder 😉)
- Grafik verbessern: z. B. mit Schatten oder Animationen.
- Soundeffekte: Pygame kann auch WAV/MP3 abspielen.
- Highscore speichern: Punkte in einer Datei oder Datenbank ablegen.

import pygame
import random
import sys
# -----------------------------
# Einstellungen
# -----------------------------
WIDTH, HEIGHT = 400, 500
PLAY_W, PLAY_H = 200, 400 # Spielfeldgröße (10 x 20 Zellen à 20px)
BLOCK_SIZE = 20
GRID_COLS, GRID_ROWS = PLAY_W // BLOCK_SIZE, PLAY_H // BLOCK_SIZE
FPS = 60
BASE_FALL_DELAY_MS = 800 # Basis-Fallzeit (ms) für Level 1
LEVEL_SPEED_FACTOR = 0.85 # pro Level wird die Fallzeit multipliziert
# Farben
COLORS = {
'bg': (18, 18, 18),
'grid': (40, 40, 40),
'text': (230, 230, 230),
'panel': (30, 30, 30),
'game_over': (200, 60, 60),
}
# Tetromino-Farben
PIECE_COLORS = [
(0, 240, 240), # I
(240, 240, 0), # O
(160, 0, 240), # T
(0, 240, 0), # S
(240, 0, 0), # Z
(0, 0, 240), # J
(240, 160, 0), # L
]
# Formen (Rotationen als Matrix von 4x4)
TETROMINOS = {
'I': [
[[0,0,0,0],
[1,1,1,1],
[0,0,0,0],
[0,0,0,0]],
[[0,0,1,0],
[0,0,1,0],
[0,0,1,0],
[0,0,1,0]],
],
'O': [
[[0,1,1,0],
[0,1,1,0],
[0,0,0,0],
[0,0,0,0]],
],
'T': [
[[0,1,0,0],
[1,1,1,0],
[0,0,0,0],
[0,0,0,0]],
[[0,1,0,0],
[0,1,1,0],
[0,1,0,0],
[0,0,0,0]],
[[0,0,0,0],
[1,1,1,0],
[0,1,0,0],
[0,0,0,0]],
[[0,1,0,0],
[1,1,0,0],
[0,1,0,0],
[0,0,0,0]],
],
'S': [
[[0,1,1,0],
[1,1,0,0],
[0,0,0,0],
[0,0,0,0]],
[[1,0,0,0],
[1,1,0,0],
[0,1,0,0],
[0,0,0,0]],
],
'Z': [
[[1,1,0,0],
[0,1,1,0],
[0,0,0,0],
[0,0,0,0]],
[[0,1,0,0],
[1,1,0,0],
[1,0,0,0],
[0,0,0,0]],
],
'J': [
[[1,0,0,0],
[1,1,1,0],
[0,0,0,0],
[0,0,0,0]],
[[0,1,1,0],
[0,1,0,0],
[0,1,0,0],
[0,0,0,0]],
[[0,0,0,0],
[1,1,1,0],
[0,0,1,0],
[0,0,0,0]],
[[0,1,0,0],
[0,1,0,0],
[1,1,0,0],
[0,0,0,0]],
],
'L': [
[[0,0,1,0],
[1,1,1,0],
[0,0,0,0],
[0,0,0,0]],
[[0,1,0,0],
[0,1,0,0],
[0,1,1,0],
[0,0,0,0]],
[[0,0,0,0],
[1,1,1,0],
[1,0,0,0],
[0,0,0,0]],
[[1,1,0,0],
[0,1,0,0],
[0,1,0,0],
[0,0,0,0]],
],
}
# -----------------------------
# Hilfsfunktionen
# -----------------------------
def new_piece():
kind = random.choice(list(TETROMINOS.keys()))
rotations = TETROMINOS[kind]
rot_idx = 0
color = PIECE_COLORS[list(TETROMINOS.keys()).index(kind)]
# Startposition: oben mittig
x = GRID_COLS // 2 - 2
y = 0
return {'kind': kind, 'rotations': rotations, 'rot': rot_idx, 'x': x, 'y': y, 'color': color}
def get_cells(piece, rot=None, x=None, y=None):
r = piece['rot'] if rot is None else rot
px = piece['x'] if x is None else x
py = piece['y'] if y is None else y
shape = piece['rotations'][r]
cells = []
for j in range(4):
for i in range(4):
if shape[j][i]:
cells.append((px + i, py + j))
return cells
def valid_position(grid, piece, rot=None, x=None, y=None):
for cx, cy in get_cells(piece, rot, x, y):
if cx < 0 or cx >= GRID_COLS or cy < 0 or cy >= GRID_ROWS:
return False
if grid[cy][cx] is not None:
return False
return True
def lock_piece(grid, piece):
for cx, cy in get_cells(piece):
if 0 <= cy < GRID_ROWS and 0 <= cx < GRID_COLS:
grid[cy][cx] = piece['color']
def clear_lines(grid):
cleared = 0
new_grid = [row for row in grid if any(cell is None for cell in row)]
cleared = GRID_ROWS - len(new_grid)
for _ in range(cleared):
new_grid.insert(0, [None for _ in range(GRID_COLS)])
return new_grid, cleared
def rotate_index(piece, direction=1):
# direction: +1 clockwise, -1 counterclockwise
rcount = len(piece['rotations'])
if rcount == 1:
return piece['rot']
return (piece['rot'] + direction) % rcount
# -----------------------------
# Zeichnen
# -----------------------------
def draw_grid(surface, origin):
ox, oy = origin
for y in range(GRID_ROWS):
for x in range(GRID_COLS):
rect = pygame.Rect(ox + x * BLOCK_SIZE, oy + y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE)
pygame.draw.rect(surface, COLORS['grid'], rect, 1)
def draw_board(surface, origin, grid):
ox, oy = origin
for y in range(GRID_ROWS):
for x in range(GRID_COLS):
color = grid[y][x]
if color:
pygame.draw.rect(surface, color, (ox + x * BLOCK_SIZE, oy + y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE))
draw_grid(surface, origin)
def draw_piece(surface, origin, piece):
ox, oy = origin
for cx, cy in get_cells(piece):
pygame.draw.rect(surface, piece['color'], (ox + cx * BLOCK_SIZE, oy + cy * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE))
def draw_panel(surface, font, score, level, lines, game_over):
panel_rect = pygame.Rect(PLAY_W + 20, 20, WIDTH - (PLAY_W + 40), HEIGHT - 40)
pygame.draw.rect(surface, COLORS['panel'], panel_rect, border_radius=8)
title = font.render("Tetris", True, COLORS['text'])
surface.blit(title, (panel_rect.x + 10, panel_rect.y + 10))
s1 = font.render(f"Punkte: {score}", True, COLORS['text'])
s2 = font.render(f"Level: {level}", True, COLORS['text'])
s3 = font.render(f"Reihen: {lines}", True, COLORS['text'])
surface.blit(s1, (panel_rect.x + 10, panel_rect.y + 50))
surface.blit(s2, (panel_rect.x + 10, panel_rect.y + 80))
surface.blit(s3, (panel_rect.x + 10, panel_rect.y + 110))
if game_over:
go = font.render("GAME OVER", True, COLORS['game_over'])
surface.blit(go, (panel_rect.x + 10, panel_rect.y + 160))
hint = font.render("R für Neustart", True, COLORS['text'])
surface.blit(hint, (panel_rect.x + 10, panel_rect.y + 190))
# -----------------------------
# Hauptspiel
# -----------------------------
def compute_fall_delay(level):
return int(BASE_FALL_DELAY_MS * (LEVEL_SPEED_FACTOR ** (level - 1)))
def main():
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Tetris (Pfeiltasten, Alt/Strg drehen)")
clock = pygame.time.Clock()
font = pygame.font.SysFont("consolas", 18)
origin = (10, 50) # linke obere Ecke des Spielfelds
# Spielfeld: GRID_ROWS x GRID_COLS mit None
grid = [[None for _ in range(GRID_COLS)] for _ in range(GRID_ROWS)]
current = new_piece()
next_piece = new_piece()
score = 0
level = 1
total_lines = 0
lines_since_levelup = 0
game_over = False
fall_delay = compute_fall_delay(level)
last_fall_time = pygame.time.get_ticks()
# Soft drop: schneller fallen, wenn Pfeil unten gehalten wird
soft_drop = False
move_left = False
move_right = False
move_repeat_delay = 100 # ms bis Wiederholung
last_move_time = 0
while True:
dt = clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit(); sys.exit()
if event.type == pygame.KEYDOWN:
if game_over:
if event.key == pygame.K_r:
# Neustart
grid = [[None for _ in range(GRID_COLS)] for _ in range(GRID_ROWS)]
current = new_piece()
next_piece = new_piece()
score = 0
level = 1
total_lines = 0
lines_since_levelup = 0
game_over = False
fall_delay = compute_fall_delay(level)
last_fall_time = pygame.time.get_ticks()
continue
if event.key == pygame.K_LEFT:
move_left = True
move_right = False
if valid_position(grid, current, x=current['x'] - 1, y=current['y']):
current['x'] -= 1
last_move_time = pygame.time.get_ticks()
elif event.key == pygame.K_RIGHT:
move_right = True
move_left = False
if valid_position(grid, current, x=current['x'] + 1, y=current['y']):
current['x'] += 1
last_move_time = pygame.time.get_ticks()
elif event.key == pygame.K_DOWN:
soft_drop = True
elif event.key in (pygame.K_LALT, pygame.K_RALT):
# Drehung im Uhrzeigersinn
new_r = rotate_index(current, +1)
if valid_position(grid, current, rot=new_r, x=current['x'], y=current['y']):
current['rot'] = new_r
else:
# kleiner "Wall kick"
if valid_position(grid, current, rot=new_r, x=current['x'] + 1, y=current['y']):
current['x'] += 1; current['rot'] = new_r
elif valid_position(grid, current, rot=new_r, x=current['x'] - 1, y=current['y']):
current['x'] -= 1; current['rot'] = new_r
elif event.key in (pygame.K_LCTRL, pygame.K_RCTRL):
# Drehung gegen den Uhrzeigersinn
new_r = rotate_index(current, -1)
if valid_position(grid, current, rot=new_r, x=current['x'], y=current['y']):
current['rot'] = new_r
else:
if valid_position(grid, current, rot=new_r, x=current['x'] + 1, y=current['y']):
current['x'] += 1; current['rot'] = new_r
elif valid_position(grid, current, rot=new_r, x=current['x'] - 1, y=current['y']):
current['x'] -= 1; current['rot'] = new_r
elif event.key == pygame.K_SPACE:
# Hard drop
drop_dist = 0
while valid_position(grid, current, x=current['x'], y=current['y'] + 1):
current['y'] += 1
drop_dist += 1
score += drop_dist # kleiner Bonus für Hard Drop
# Locken
lock_piece(grid, current)
grid, cleared = clear_lines(grid)
if cleared:
total_lines += cleared
lines_since_levelup += cleared
# klassisches Scoring: 1/3/5/8 pro Linie nacheinander (hier einfach * 100)
if cleared == 1:
score += 100
elif cleared == 2:
score += 300
elif cleared == 3:
score += 500
elif cleared >= 4:
score += 800
if lines_since_levelup >= 10:
level += 1
lines_since_levelup -= 10
fall_delay = compute_fall_delay(level)
current = next_piece
next_piece = new_piece()
# Game Over prüfen
if not valid_position(grid, current, x=current['x'], y=current['y']):
game_over = True
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT:
move_left = False
elif event.key == pygame.K_RIGHT:
move_right = False
elif event.key == pygame.K_DOWN:
soft_drop = False
if not game_over:
# horizontale Wiederholung
now = pygame.time.get_ticks()
if move_left and now - last_move_time > move_repeat_delay:
if valid_position(grid, current, x=current['x'] - 1, y=current['y']):
current['x'] -= 1
last_move_time = now
if move_right and now - last_move_time > move_repeat_delay:
if valid_position(grid, current, x=current['x'] + 1, y=current['y']):
current['x'] += 1
last_move_time = now
# Fallen
current_fall_delay = int(fall_delay * (0.35 if soft_drop else 1.0))
if now - last_fall_time >= current_fall_delay:
last_fall_time = now
if valid_position(grid, current, x=current['x'], y=current['y'] + 1):
current['y'] += 1
else:
# Landen und sperren
lock_piece(grid, current)
grid, cleared = clear_lines(grid)
if cleared:
total_lines += cleared
lines_since_levelup += cleared
if cleared == 1:
score += 100
elif cleared == 2:
score += 300
elif cleared == 3:
score += 500
elif cleared >= 4:
score += 800
if lines_since_levelup >= 10:
level += 1
lines_since_levelup -= 10
fall_delay = compute_fall_delay(level)
current = next_piece
next_piece = new_piece()
# Game Over, wenn neue Figur nicht passt
if not valid_position(grid, current, x=current['x'], y=current['y']):
game_over = True
# Zeichnen
screen.fill(COLORS['bg'])
draw_board(screen, origin, grid)
if not game_over:
draw_piece(screen, origin, current)
# Vorschau (Next) zeichnen
preview_rect = pygame.Rect(PLAY_W + 20, HEIGHT - 140, WIDTH - (PLAY_W + 40), 120)
pygame.draw.rect(screen, COLORS['panel'], preview_rect, border_radius=8)
preview_label = font.render("Nächster Stein", True, COLORS['text'])
screen.blit(preview_label, (preview_rect.x + 10, preview_rect.y + 10))
# kleine Vorschau zeichnen
tmp_piece = dict(next_piece)
tmp_piece['x'] = GRID_COLS + 1 # außerhalb des Boards; wir zeichnen direkt relativ zur preview_rect
tmp_piece['y'] = GRID_ROWS + 1
# manuell: iteriere die Form und zeichne relativ zur preview_rect
shape = next_piece['rotations'][next_piece['rot']]
start_x = preview_rect.x + 10
start_y = preview_rect.y + 40
for j in range(4):
for i in range(4):
if shape[j][i]:
pygame.draw.rect(screen, next_piece['color'],
(start_x + i * BLOCK_SIZE, start_y + j * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE))
draw_panel(screen, font, score, level, total_lines, game_over)
pygame.display.flip()
if __name__ == "__main__":
main()
8. Starten
Speichere den Code als tetris.py und starte ihn mit:
python tetris.py© 2025 MaDe-Online

