IT 관련/기타

Claude 가 만들어 준 Tetris(python)

islet2 2025. 2. 18. 22:38

게임 실행 화면
tetris.py
0.02MB

 

* 클로드 & 코파일럿이 함께 만든 Tetris 파이썬 코드입니다.

* 전 파이썬의 기본문법만 알고 있어요.

* 아직 완성도는 부족하지만 수정하면서 게임하시기에는 재미있을 꺼예요.

  (이상한 블록을 추가 할 수도 있구요. ㅎ)

 

# Description: 테트리스 게임을 구현한 코드입니다.
import pygame
import random
import time

# 초기화
pygame.init()

# 색상 정의
COLORS = {
    'BLACK': (0, 0, 0),
    'WHITE': (255, 255, 255),
    'GRAY': (128, 128, 128),
    'LIGHT_GRAY': (192, 192, 192),
    'RED': (255, 0, 0),
    'GREEN': (0, 255, 0),
    'BLUE': (0, 0, 255),
    'CYAN': (0, 255, 255),
    'MAGENTA': (255, 0, 255),
    'YELLOW': (255, 255, 0),
    'ORANGE': (255, 165, 0)
}

# 테트리미노 모양 정의
SHAPES = [
    [[1, 1, 1, 1]],  # I
    [[1, 1], [1, 1]],  # O
    [[1, 1, 1], [0, 1, 0]],  # T
    [[1, 1, 1], [1, 0, 0]],  # L
    [[1, 1, 1], [0, 0, 1]],  # J
    [[1, 1, 0], [0, 1, 1]],  # S
    [[0, 1, 1], [1, 1, 0]]   # Z
]

class Tetris:
    def __init__(self):
        self.width = 600
        self.height = 600
        self.grid_size = 25
        self.grid_width = 10
        self.grid_height = 20
        self.screen = pygame.display.set_mode((self.width, self.height))
        pygame.display.set_caption('Tetris')
       
        # 게임 상태 초기화
        self.reset_game()
        self.game_state = "START"  # START, PLAYING, PAUSED, GAMEOVER
       
    def reset_game(self):
        # 게임 보드 초기화
        self.board = [[0] * self.grid_width for _ in range(self.grid_height)]
        self.current_piece = None
        self.next_piece = self.new_piece()
        self.held_piece = None
        self.can_hold = True
        self.score = 0
        self.level = 1
        self.lines_cleared = 0
        self.show_ghost = True
        self.base_fall_time = 1.0
        self.fall_time = self.base_fall_time
        self.last_fall = time.time()
       
    def new_piece(self):
        # 새로운 테트리미노 생성
        shape = random.choice(SHAPES)
        color = random.choice(list(COLORS.values())[4:])  # 기본 색상 제외
        return {
            'shape': shape,
            'color': color,
            'x': self.grid_width // 2 - len(shape[0]) // 2,
            'y': 0,
            'rotation': 0
        }
   
    def draw_text(self, text, size, color, x, y, center=True):
        # 텍스트를 화면에 그림
        font = pygame.font.Font(None, size)
        text_surface = font.render(text, True, color)
        if center:
            text_rect = text_surface.get_rect(center=(x, y))
        else:
            text_rect = text_surface.get_rect(topleft=(x, y))
        self.screen.blit(text_surface, text_rect)
        return text_rect.bottom

    def draw_info_box(self, title, content, x, y, width, height):
        # 정보 박스를 그림
        pygame.draw.rect(self.screen, COLORS['WHITE'], (x, y, width, height), 1)
        title_y = self.draw_text(title, 24, COLORS['WHITE'], x + width//2, y - 15)
        if content:
            for i in range(len(content['shape'])):
                for j in range(len(content['shape'][0])):
                    if content['shape'][i][j]:
                        pygame.draw.rect(self.screen, content['color'],
                                      (x + (width - len(content['shape'][0]) * self.grid_size)//2 + j * self.grid_size,
                                       y + (height - len(content['shape']) * self.grid_size)//2 + i * self.grid_size,
                                       self.grid_size - 1, self.grid_size - 1))
   
    def draw(self):
        # 화면을 검은색으로 채움
        self.screen.fill(COLORS['BLACK'])
       
        # 게임 상태가 "START"일 때
        if self.game_state == "START":
            # "TETRIS" 텍스트를 화면 중앙에 그림
            self.draw_text("TETRIS", 72, COLORS['WHITE'], self.width//2, self.height//3, center=True)
            # 깜박이는 효과로 "PRESS ANY KEY TO START" 텍스트를 화면 중앙에 그림
            if int(time.time() * 2) % 2:
                self.draw_text("PRESS ANY KEY TO START", 36, COLORS['WHITE'], self.width//2, self.height//2, center=True)
            pygame.display.flip()
            return
       
        # 게임 상태가 "PAUSED"일 때
        if self.game_state == "PAUSED":
            # "PAUSED" 텍스트를 화면 중앙에 그림
            self.draw_text("PAUSED", 72, COLORS['WHITE'], self.width//2, self.height//2, center=True)
            pygame.display.flip()
            return
       
        # 게임 상태가 "GAMEOVER"일 때
        if self.game_state == "GAMEOVER":
            # "GAME OVER" 텍스트를 화면 중앙에 그림
            self.draw_text("GAME OVER", 72, COLORS['RED'], self.width//2, self.height//3, center=True)
            # "Press R to Restart or Q to Quit" 텍스트를 화면 중앙에 그림
            self.draw_text("Press R to Restart or Q to Quit", 36, COLORS['WHITE'], self.width//2, self.height//2, center=True)
            pygame.display.flip()
            return
       
        # 게임 보드의 왼쪽 상단 좌표 계산
        board_left = (self.width - self.grid_width * self.grid_size) // 2
        board_top = (self.height - self.grid_height * self.grid_size) // 2
       
        # 게임 보드 배경을 그림
        pygame.draw.rect(self.screen, (30,30,30),
                         (board_left - 1, board_top - 1,
                          self.grid_width * self.grid_size + 2,
                          self.grid_height * self.grid_size + 2))
       
        # 보드에 있는 기존 블록들을 그림
        for y in range(self.grid_height):
            for x in range(self.grid_width):
                if self.board[y][x]:
                    pygame.draw.rect(self.screen, self.board[y][x],
                                     (board_left + x * self.grid_size,
                                      board_top + y * self.grid_size,
                                      self.grid_size - 1, self.grid_size - 1))
       
        # 고스트 블록을 그림
        if self.show_ghost and self.current_piece:
            ghost_y = self.get_ghost_position()
            for i in range(len(self.current_piece['shape'])):
                for j in range(len(self.current_piece['shape'][0])):
                    if self.current_piece['shape'][i][j]:
                        pygame.draw.rect(self.screen, COLORS['GRAY'],
                                         (board_left + (self.current_piece['x'] + j) * self.grid_size,
                                          board_top + (ghost_y + i) * self.grid_size,
                                          self.grid_size - 1, self.grid_size - 1), 1)
       
        # 현재 블록을 그림
        if self.current_piece:
            for i in range(len(self.current_piece['shape'])):
                for j in range(len(self.current_piece['shape'][0])):
                    if self.current_piece['shape'][i][j]:
                        pygame.draw.rect(self.screen, self.current_piece['color'],
                                         (board_left + (self.current_piece['x'] + j) * self.grid_size,
                                          board_top + (self.current_piece['y'] + i) * self.grid_size,
                                          self.grid_size - 1, self.grid_size - 1))
       
        # 정보 패널의 왼쪽 상단 좌표 계산
        info_left = board_left + (self.grid_width + 1) * self.grid_size
        info_top = board_top
        box_size = self.grid_size * 4
       
        # NEXT 박스를 그림
        self.draw_info_box("NEXT", self.next_piece, info_left, info_top, box_size, box_size)
       
        # HOLD 박스를 그림
        self.draw_info_box("HOLD", self.held_piece, info_left, info_top + box_size + 30, box_size, box_size)
       
        # 컨트롤 정보를 그림
        controls_y = info_top + 2 * box_size + 60
        self.draw_text("H: Hold", 24, COLORS['WHITE'], info_left + box_size//2, controls_y, True)
        self.draw_text("G: Ghost", 24, COLORS['WHITE'], info_left + box_size//2, controls_y + 30, True)
       
        # 레벨과 점수를 그림
        level_y = controls_y + 80
        self.draw_text(f"LEVEL: {self.level}", 24, COLORS['WHITE'], info_left + box_size//2, level_y, True)
        self.draw_text(f"SCORE: {self.score}", 24, COLORS['WHITE'], info_left + box_size//2, level_y + 30, True)
       
        # 화면 업데이트
        pygame.display.flip()

    def run(self):
        clock = pygame.time.Clock()
        running = True
       
        while running:
            if self.game_state == "START":
                self.draw()
                for event in pygame.event.get():
                    if event.type == pygame.QUIT:
                        running = False
                    elif event.type == pygame.KEYDOWN:
                        self.game_state = "PLAYING"
                        self.current_piece = self.new_piece()
                continue
               
            if self.game_state == "GAMEOVER":
                self.draw()
                for event in pygame.event.get():
                    if event.type == pygame.QUIT:
                        running = False
                    elif event.type == pygame.KEYDOWN:
                        if event.key == pygame.K_r:
                            self.reset_game()
                            self.game_state = "PLAYING"
                            self.current_piece = self.new_piece()
                        elif event.key == pygame.K_q:
                            running = False
                continue
               
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    running = False
                elif event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_p:
                        self.game_state = "PAUSED" if self.game_state == "PLAYING" else "PLAYING"
                    elif self.game_state == "PLAYING":
                        if event.key == pygame.K_LEFT:
                            if self.valid_move(self.current_piece,
                                               self.current_piece['x'] - 1,
                                               self.current_piece['y']):
                                self.current_piece['x'] -= 1
                        elif event.key == pygame.K_RIGHT:
                            if self.valid_move(self.current_piece,
                                               self.current_piece['x'] + 1,
                                               self.current_piece['y']):
                                self.current_piece['x'] += 1
                        elif event.key == pygame.K_DOWN:
                            if self.valid_move(self.current_piece,
                                               self.current_piece['x'],
                                               self.current_piece['y'] + 1):
                                self.current_piece['y'] += 1
                        elif event.key == pygame.K_UP:
                            rotated = self.rotate_piece(self.current_piece)
                            if self.valid_move({'shape': rotated,
                                                'x': self.current_piece['x'],
                                                'y': self.current_piece['y']},
                                               self.current_piece['x'],
                                               self.current_piece['y']):
                                self.current_piece['shape'] = rotated
                        elif event.key == pygame.K_SPACE:
                            while self.valid_move(self.current_piece,
                                                  self.current_piece['x'],
                                                  self.current_piece['y'] + 1):
                                self.current_piece['y'] += 1
                        elif event.key == pygame.K_h:
                            self.hold_piece()
                        elif event.key == pygame.K_g:
                            self.show_ghost = not self.show_ghost
           
            if self.game_state == "PLAYING":
                if time.time() - self.last_fall > self.fall_time:
                    if self.valid_move(self.current_piece,
                                       self.current_piece['x'],
                                       self.current_piece['y'] + 1):
                        self.current_piece['y'] += 1
                    else:
                        for i in range(len(self.current_piece['shape'])):
                            for j in range(len(self.current_piece['shape'][0])):
                                if self.current_piece['shape'][i][j]:
                                    self.board[self.current_piece['y'] + i][self.current_piece['x'] + j] = self.current_piece['color']
                       
                        self.clear_lines()
                        self.current_piece = self.next_piece
                        self.next_piece = self.new_piece()
                        self.can_hold = True
                       
                        if not self.valid_move(self.current_piece,
                                               self.current_piece['x'],
                                               self.current_piece['y']):
                            self.game_state = "GAMEOVER"
                   
                    self.last_fall = time.time()
           
            self.draw()
            clock.tick(60)
       
        pygame.quit()

    # [이전에 정의된 나머지 메서드들은 그대로 유지]
    def rotate_piece(self, piece):
        shape = piece['shape']
        #시계 방향 회전
        #return [[shape[y][x] for y in range(len(shape)-1, -1, -1)] for x in range(len(shape[0]))]
        # 시계 반대방향으로 회전
        return [[shape[y][x] for y in range(len(shape))] for x in range(len(shape[0])-1, -1, -1)]
   
    def valid_move(self, piece, x, y):
        for i in range(len(piece['shape'])):
            for j in range(len(piece['shape'][0])):
                if piece['shape'][i][j]:
                    new_x = x + j
                    new_y = y + i
                    if (new_x < 0 or new_x >= self.grid_width or
                        new_y >= self.grid_height or
                        (new_y >= 0 and self.board[new_y][new_x])):
                        return False
        return True
   
    def get_ghost_position(self):
        if not self.current_piece:
            return None
       
        ghost_piece = self.current_piece.copy()
        ghost_y = ghost_piece['y']
       
        while self.valid_move(ghost_piece, ghost_piece['x'], ghost_y + 1):
            ghost_y += 1
           
        return ghost_y
   
    def hold_piece(self):
        if not self.can_hold:
            return
           
        if self.held_piece is None:
            self.held_piece = self.current_piece
            self.current_piece = self.next_piece
            self.next_piece = self.new_piece()
        else:
            self.held_piece, self.current_piece = self.current_piece, self.held_piece
           
        self.current_piece['x'] = self.grid_width // 2 - len(self.current_piece['shape'][0]) // 2
        self.current_piece['y'] = 0
        self.can_hold = False
   
    def clear_lines(self):
        # lines = 0
        # y = self.grid_height - 1
        # while y >= 0:
        #     if all(self.board[y]):
        #         lines += 1
        #         del self.board[y]
        #         self.board.insert(0, [0] * self.grid_width)
        #     else:
        #         y -= 1
               
        # if lines > 0:
        #     score_multiplier = {1: 100, 2: 300, 3: 500, 4: 800}
        #     self.score += score_multiplier.get(lines, 1000) * self.level
        #     self.lines_cleared += lines
           
        #     self.level = min(20, 1 + self.lines_cleared // 10)
        #     self.fall_time = self.base_fall_time * (0.8 ** (self.level - 1))
        lines = 0
        new_board = [row for row in self.board if any(cell == 0 for cell in row)]
        lines = self.grid_height - len(new_board)
        new_board = [[0] * self.grid_width for _ in range(lines)] + new_board
   
        if lines > 0:
            self.board = new_board
            score_multiplier = {1: 100, 2: 300, 3: 500, 4: 800}
            self.score += score_multiplier.get(lines, 1000) * self.level
            self.lines_cleared += lines
           
            self.level = min(20, 1 + self.lines_cleared // 10)
            self.fall_time = self.base_fall_time * (0.8 ** (self.level - 1))

if __name__ == '__main__':
    game = Tetris()
    game.run()

 

 

 

'IT 관련 > 기타' 카테고리의 다른 글

세계시간 코드 설명  (0) 2025.03.14
세계시간  (0) 2025.03.14
Python에서 외부 모듈을 가져오는 방법 정리  (0) 2025.03.11
간단 버전 flappy bird (html)  (0) 2025.02.28
Claude 가 만들어 준 Tetris (웹버젼)  (0) 2025.02.18