반응형
파이썬으로 만든 테트리스 게임 소스입니다.
하단에서 소스 파일을 다운로드할 수 있습니다.
pygame이 설치되어 있어야 합니다.
pip install pygame
pip를 실행하는 방법을 모르면 아래 글을 읽어보세요.
https://coding-abc.tistory.com/349
파이썬, pip: 패키지 및 라이브러리를 설치하고 관리하기
pip는 **"Python Package Installer"**의 약자로, Python 패키지 및 라이브러리를 설치하고 관리하기 위한 표준 도구입니다. Python의 공식 패키지 저장소인 **PyPI (Python Package Index)**에서 패키지를 다운로드하
coding-abc.tistory.com
테트리스 게임 소스 Tetris game source
조작 키:
- ← / → : 이동
- ↓ : 소프트 드롭
- Space : 하드 드롭
- ↑ 또는 X : 시계 방향 회전
- Z : 반시계 회전
- C : 홀드
- P : 일시정지
- Esc : 종료
"""
Simple Tetris game in Python using pygame
Save as tetris.py and run with: python tetris.py
Requires: pip install pygame
Controls:
- Left/Right arrow: move
- Down arrow: soft drop
- Space: hard drop
- Up arrow / X: rotate clockwise
- Z: rotate counter-clockwise
- C: hold piece
- P: pause
- Esc: quit
This is a single-file implementation intended for learning and small projects.
"""
import pygame
import random
import sys
# Game constants
CELL_SIZE = 30
COLUMNS = 10
ROWS = 20
WIDTH = CELL_SIZE * COLUMNS
HEIGHT = CELL_SIZE * ROWS
FPS = 60
# Colors
BLACK = (0, 0, 0)
GRAY = (128, 128, 128)
WHITE = (255, 255, 255)
# Tetromino shapes (4x4 matrices)
SHAPES = {
'I': [[0,0,0,0],
[1,1,1,1],
[0,0,0,0],
[0,0,0,0]],
'J': [[1,0,0],
[1,1,1],
[0,0,0]],
'L': [[0,0,1],
[1,1,1],
[0,0,0]],
'O': [[1,1],
[1,1]],
'S': [[0,1,1],
[1,1,0],
[0,0,0]],
'T': [[0,1,0],
[1,1,1],
[0,0,0]],
'Z': [[1,1,0],
[0,1,1],
[0,0,0]],
}
COLORS = {
'I': (0, 240, 240),
'J': (0, 0, 240),
'L': (240, 160, 0),
'O': (240, 240, 0),
'S': (0, 240, 0),
'T': (160, 0, 240),
'Z': (240, 0, 0),
}
# Helper functions
def rotate(shape):
# rotate shape clockwise
return [list(row) for row in zip(*shape[::-1])]
def create_grid():
return [[None for _ in range(COLUMNS)] for _ in range(ROWS)]
class Piece:
def __init__(self, kind=None):
if kind is None:
kind = random.choice(list(SHAPES.keys()))
self.kind = kind
self.shape = [row[:] for row in SHAPES[kind]]
self.color = COLORS[kind]
# start position (row, col) - top-left of shape matrix in the board
self.row = 0
self.col = COLUMNS // 2 - len(self.shape[0]) // 2
def rotate(self):
self.shape = rotate(self.shape)
def width(self):
return len(self.shape[0])
def height(self):
return len(self.shape)
class Tetris:
def __init__(self):
self.grid = create_grid()
self.current = Piece()
self.next_piece = Piece()
self.hold_piece = None
self.can_hold = True
self.score = 0
self.level = 1
self.lines = 0
self.game_over = False
self.drop_counter = 0.0
self.drop_speed = 0.8 # seconds per cell (will decrease as level increases)
def new_piece(self):
self.current = self.next_piece
self.next_piece = Piece()
self.can_hold = True
if not self.valid_position(self.current, self.current.row, self.current.col):
self.game_over = True
def hold(self):
if not self.can_hold:
return
self.can_hold = False
if self.hold_piece is None:
self.hold_piece = Piece(self.current.kind)
self.new_piece()
else:
temp = Piece(self.current.kind)
self.current = Piece(self.hold_piece.kind)
self.hold_piece = temp
# reset position of current piece
self.current.row = 0
self.current.col = COLUMNS // 2 - len(self.current.shape[0]) // 2
if not self.valid_position(self.current, self.current.row, self.current.col):
self.game_over = True
def valid_position(self, piece, test_row, test_col):
for r, row in enumerate(piece.shape):
for c, val in enumerate(row):
if val:
board_r = test_row + r
board_c = test_col + c
if (board_c < 0 or board_c >= COLUMNS or
board_r >= ROWS):
return False
if board_r >= 0 and self.grid[board_r][board_c] is not None:
return False
return True
def lock_piece(self):
piece = self.current
for r, row in enumerate(piece.shape):
for c, val in enumerate(row):
if val:
board_r = piece.row + r
board_c = piece.col + c
if 0 <= board_r < ROWS and 0 <= board_c < COLUMNS:
self.grid[board_r][board_c] = piece.color
self.clear_lines()
self.new_piece()
def clear_lines(self):
full_rows = [r for r in range(ROWS) if all(self.grid[r][c] is not None for c in range(COLUMNS))]
lines_cleared = len(full_rows)
if lines_cleared > 0:
for r in reversed(full_rows):
del self.grid[r]
self.grid.insert(0, [None for _ in range(COLUMNS)])
# scoring (classic tetris style)
points = {1: 100, 2: 300, 3: 500, 4: 800}
self.score += points.get(lines_cleared, lines_cleared * 200) * self.level
self.lines += lines_cleared
# level up per 10 lines
self.level = 1 + self.lines // 10
# increase speed
self.drop_speed = max(0.05, 0.8 - (self.level - 1) * 0.06)
def hard_drop(self):
while self.valid_position(self.current, self.current.row + 1, self.current.col):
self.current.row += 1
self.lock_piece()
def soft_drop(self):
if self.valid_position(self.current, self.current.row + 1, self.current.col):
self.current.row += 1
self.score += 1 # small reward
else:
self.lock_piece()
def move(self, dx):
if self.valid_position(self.current, self.current.row, self.current.col + dx):
self.current.col += dx
def rotate_cw(self):
original = [row[:] for row in self.current.shape]
self.current.rotate()
# wall kick simple: try offsets
kicks = [(0,0), (0,-1), (0,1), (-1,0), (1,0), (0,-2), (0,2)]
for dr, dc in kicks:
if self.valid_position(self.current, self.current.row + dr, self.current.col + dc):
self.current.row += dr
self.current.col += dc
return
# if none valid, revert
self.current.shape = original
def rotate_ccw(self):
# rotate counter-clockwise = 3 cw
for _ in range(3):
self.rotate_cw()
def tick(self, dt):
if self.game_over:
return
self.drop_counter += dt
if self.drop_counter >= self.drop_speed:
self.drop_counter = 0.0
# gravity step
if self.valid_position(self.current, self.current.row + 1, self.current.col):
self.current.row += 1
else:
self.lock_piece()
# Pygame rendering
def draw_grid(surface, grid):
for r in range(ROWS):
for c in range(COLUMNS):
rect = pygame.Rect(c * CELL_SIZE, r * CELL_SIZE, CELL_SIZE, CELL_SIZE)
pygame.draw.rect(surface, GRAY, rect, 1)
if grid[r][c] is not None:
pygame.draw.rect(surface, grid[r][c], rect.inflate(-2, -2))
def draw_piece(surface, piece):
for r, row in enumerate(piece.shape):
for c, val in enumerate(row):
if val:
board_r = piece.row + r
board_c = piece.col + c
if board_r >= 0:
rect = pygame.Rect(board_c * CELL_SIZE, board_r * CELL_SIZE, CELL_SIZE, CELL_SIZE)
pygame.draw.rect(surface, piece.color, rect.inflate(-2, -2))
def draw_next(surface, piece, x, y):
font = pygame.font.SysFont(None, 20)
txt = font.render('Next', True, WHITE)
surface.blit(txt, (x, y - 24))
for r, row in enumerate(piece.shape):
for c, val in enumerate(row):
if val:
rect = pygame.Rect(x + c * CELL_SIZE, y + r * CELL_SIZE, CELL_SIZE, CELL_SIZE)
pygame.draw.rect(surface, piece.color, rect.inflate(-2, -2))
pygame.draw.rect(surface, GRAY, rect, 1)
def draw_hold(surface, piece, x, y):
font = pygame.font.SysFont(None, 20)
txt = font.render('Hold', True, WHITE)
surface.blit(txt, (x, y - 24))
if piece is None:
return
for r, row in enumerate(piece.shape):
for c, val in enumerate(row):
if val:
rect = pygame.Rect(x + c * CELL_SIZE, y + r * CELL_SIZE, CELL_SIZE, CELL_SIZE)
pygame.draw.rect(surface, piece.color, rect.inflate(-2, -2))
pygame.draw.rect(surface, GRAY, rect, 1)
def draw_info(surface, game, x, y):
font = pygame.font.SysFont(None, 24)
lines = [f'Score: {game.score}', f'Level: {game.level}', f'Lines: {game.lines}']
for i, line in enumerate(lines):
txt = font.render(line, True, WHITE)
surface.blit(txt, (x, y + i * 28))
def main():
pygame.init()
screen = pygame.display.set_mode((WIDTH + 200, HEIGHT))
pygame.display.set_caption('Tetris')
clock = pygame.time.Clock()
game = Tetris()
running = True
paused = False
key_down_time = 0
move_delay = 0.12 # for holding left/right
last_move_time = 0
while running:
dt = clock.tick(FPS) / 1000.0
'''for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
elif event.key == pygame.K_p:
paused = not paused
elif event.key == pygame.K_SPACE:
game.hard_drop()
elif event.key == pygame.K_DOWN:
game.soft_drop()
elif event.key == pygame.K_LEFT:
game.move(-1)
elif event.key == pygame.K_RIGHT:
game.move(1)
elif event.key == pygame.K_UP or event.key == pygame.K_x:
game.rotate_cw()
elif event.key == pygame.K_z:
game.rotate_ccw()
elif event.key == pygame.K_c:
game.hold()
'''
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
elif event.key == pygame.K_p:
paused = not paused
elif event.key == pygame.K_SPACE:
game.hard_drop()
elif event.key == pygame.K_DOWN:
game.soft_drop()
elif event.key == pygame.K_LEFT:
game.move(-1)
last_move_time = pygame.time.get_ticks() / 1000.0 # ← 키 누른 시점 저장
elif event.key == pygame.K_RIGHT:
game.move(1)
last_move_time = pygame.time.get_ticks() / 1000.0
elif event.key == pygame.K_UP or event.key == pygame.K_x:
game.rotate_cw()
elif event.key == pygame.K_z:
game.rotate_ccw()
elif event.key == pygame.K_c:
game.hold()
keys = pygame.key.get_pressed()
if not paused and not game.game_over:
if keys[pygame.K_LEFT]:
# repeat behavior
if pygame.time.get_ticks() / 1000.0 - last_move_time > move_delay:
game.move(-1)
last_move_time = pygame.time.get_ticks() / 1000.0
if keys[pygame.K_RIGHT]:
if pygame.time.get_ticks() / 1000.0 - last_move_time > move_delay:
game.move(1)
last_move_time = pygame.time.get_ticks() / 1000.0
if not paused and not game.game_over:
game.tick(dt)
# draw
screen.fill(BLACK)
# playfield surface
play_surf = pygame.Surface((WIDTH, HEIGHT))
play_surf.fill((20, 20, 20))
draw_grid(play_surf, game.grid)
draw_piece(play_surf, game.current)
screen.blit(play_surf, (0, 0))
# side panel
side_x = WIDTH + 20
draw_next(screen, game.next_piece, side_x, 20)
draw_hold(screen, game.hold_piece, side_x, 140)
draw_info(screen, game, side_x, 260)
if paused:
font = pygame.font.SysFont(None, 48)
txt = font.render('PAUSED', True, WHITE)
screen.blit(txt, (WIDTH // 2 - 80, HEIGHT // 2 - 24))
if game.game_over:
font = pygame.font.SysFont(None, 48)
txt = font.render('GAME OVER', True, WHITE)
screen.blit(txt, (WIDTH // 2 - 140, HEIGHT // 2 - 24))
pygame.display.flip()
pygame.quit()
sys.exit()
if __name__ == '__main__':
main()
소스 파일 다운로드:
반응형
'Python' 카테고리의 다른 글
파이썬: 파일명을 입력하면 워드클라우드(Word Cloud) 이미지 생성 (8) | 2025.08.17 |
---|---|
파이썬, 슈팅 (갤러그) 게임 소스 (0) | 2025.01.13 |
파이썬: 테트리스 소스 (1) | 2024.12.18 |
파이썬, raw 이미지 출력하기 (0) | 2024.12.06 |
파이썬, 문장에서 단어별 빈도수 카운트 Counter collections (1) | 2024.11.19 |