파이썬 AI 실습/그록3로 5분만에 테트리스+비주얼드 게임만들기

그록3로 5분만에 테트리스+비주얼드 게임만들기

파기차차 2025. 3. 1. 15:12
728x90
반응형
SMALL

ㅁ 개요

 

O 프로그램 소개

 

 - 이번 내용은 약 1주일전 출시된 그록3를 활용하여 출시 당시 시연했던 테트리스+비주얼드 게임을 5분 내외로 만는 방법에 대하여 알아보겠습니다.

 

 

 

 


 

 

1.아래 사이트에 접속 후 왼쪽 메뉴에서 'Grok' 를 클릭 합니다.

 

https://x.com

 

 

 

 2. 아래와 같이 'Grok3 beta'가 선택된 상태에서 "테스트리와 ~~~"  요청 글을 입력합니다.

 

 

 

소스코드 1(12-2.py)

import pygame
import random

# 초기화
pygame.init()

# 화면 설정
WIDTH, HEIGHT = 400, 600
GRID_SIZE = 30
GRID_WIDTH = WIDTH // GRID_SIZE
GRID_HEIGHT = HEIGHT // GRID_SIZE
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Tetris Gems")

# 색상 정의
COLORS = [(255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 255, 0)]  # 빨강, 초록, 파랑, 노랑
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)

# 테트리스 블록 모양 (7가지 테트로미노)
SHAPES = [
    [[1, 1, 1, 1]],  # I
    [[1, 1], [1, 1]],  # O
    [[0, 1, 0], [1, 1, 1]],  # T
    [[0, 1, 1], [1, 1, 0]],  # S
    [[1, 1, 0], [0, 1, 1]],  # Z
    [[1, 0, 0], [1, 1, 1]],  # J
    [[0, 0, 1], [1, 1, 1]]   # L
]

# 게임 보드 초기화
board = [[None for _ in range(GRID_WIDTH)] for _ in range(GRID_HEIGHT)]

# 블록 클래스
class Block:
    def __init__(self):
        self.shape = random.choice(SHAPES)
        self.colors = [[random.choice(COLORS) if cell else None for cell in row] for row in self.shape]
        self.x = GRID_WIDTH // 2 - len(self.shape[0]) // 2
        self.y = 0

    def draw(self):
        for i, row in enumerate(self.shape):
            for j, cell in enumerate(row):
                if cell:
                    pygame.draw.rect(screen, self.colors[i][j],
                                     (self.x * GRID_SIZE + j * GRID_SIZE, 
                                      self.y * GRID_SIZE + i * GRID_SIZE, 
                                      GRID_SIZE - 1, GRID_SIZE - 1))

    def move(self, dx, dy):
        self.x += dx
        self.y += dy

    def rotate(self):
        # 90도 회전 (행렬 전치 후 좌우 반전)
        new_shape = [[self.shape[len(self.shape) - 1 - j][i] for j in range(len(self.shape))] 
                     for i in range(len(self.shape[0]))]
        new_colors = [[self.colors[len(self.shape) - 1 - j][i] for j in range(len(self.shape))] 
                      for i in range(len(self.shape[0]))]
        old_shape, old_colors = self.shape, self.colors
        self.shape, self.colors = new_shape, new_colors
        if self.collides():
            self.shape, self.colors = old_shape, old_colors  # 충돌 시 회전 취소

    def collides(self):
        for i, row in enumerate(self.shape):
            for j, cell in enumerate(row):
                if cell:
                    new_x = self.x + j
                    new_y = self.y + i
                    if (new_x < 0 or new_x >= GRID_WIDTH or 
                        new_y >= GRID_HEIGHT or 
                        (new_y >= 0 and board[new_y][new_x] is not None)):
                        return True
        return False

    def place(self):
        for i, row in enumerate(self.shape):
            for j, cell in enumerate(row):
                if cell:
                    board[self.y + i][self.x + j] = self.colors[i][j]

# 매치-3 체크 및 제거
def check_matches():
    matches = []
    # 가로 체크
    for i in range(GRID_HEIGHT):
        for j in range(GRID_WIDTH - 2):
            if (board[i][j] and board[i][j + 1] and board[i][j + 2] and
                board[i][j] == board[i][j + 1] == board[i][j + 2]):
                matches.extend([(i, j), (i, j + 1), (i, j + 2)])
    # 세로 체크
    for i in range(GRID_HEIGHT - 2):
        for j in range(GRID_WIDTH):
            if (board[i][j] and board[i + 1][j] and board[i + 2][j] and
                board[i][j] == board[i + 1][j] == board[i + 2][j]):
                matches.extend([(i, j), (i + 1, j), (i + 2, j)])

    # 매치 제거
    for i, j in matches:
        board[i][j] = None

    # 블록 아래로 떨어뜨리기
    for j in range(GRID_WIDTH):
        for i in range(GRID_HEIGHT - 1, -1, -1):
            if board[i][j] is None:
                for k in range(i - 1, -1, -1):
                    if board[k][j] is not None:
                        board[i][j] = board[k][j]
                        board[k][j] = None
                        break

# 메인 게임 루프
def main():
    clock = pygame.time.Clock()
    current_block = Block()
    fall_time = 0
    fall_speed = 500  # 밀리초 단위

    running = True
    while running:
        fall_time += clock.get_rawtime()
        clock.tick(60)

        # 이벤트 처리
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_LEFT and not current_block.collides():
                    current_block.move(-1, 0)
                    if current_block.collides():
                        current_block.move(1, 0)
                if event.key == pygame.K_RIGHT and not current_block.collides():
                    current_block.move(1, 0)
                    if current_block.collides():
                        current_block.move(-1, 0)
                if event.key == pygame.K_DOWN:
                    current_block.move(0, 1)
                    if current_block.collides():
                        current_block.move(0, -1)
                        current_block.place()
                        check_matches()
                        current_block = Block()
                if event.key == pygame.K_UP:  # 위쪽 화살표로 회전
                    current_block.rotate()

        # 블록 자동 하강
        if fall_time >= fall_speed:
            current_block.move(0, 1)
            if current_block.collides():
                current_block.move(0, -1)
                current_block.place()
                check_matches()
                current_block = Block()
            fall_time = 0

        # 화면 그리기
        screen.fill(BLACK)
        for i in range(GRID_HEIGHT):
            for j in range(GRID_WIDTH):
                if board[i][j]:
                    pygame.draw.rect(screen, board[i][j],
                                     (j * GRID_SIZE, i * GRID_SIZE, GRID_SIZE - 1, GRID_SIZE - 1))
        current_block.draw()
        pygame.display.flip()

    pygame.quit()

if __name__ == "__main__":
    main()

 

 

 

3.위의 소스코드를 복사하여 비주얼스튜디오코드 등 편집기에 붙여넣고 실행하면 아래와 같이 실행되는 것을 볼 수 있습니다.  하지만, 화살표 아래키를 눌러도 즉시 블록이 내려가지 않습니다.

 

 

 

 4.위 3의 단점을 보완하기위해 다시 그록3에게 아래와 같이 보완요청을 합니다.

"화살표 아래키 ~~~"

 

 

 

소스코드 2 (12-3.py)

import pygame
import random

# 초기화
pygame.init()

# 화면 설정
WIDTH, HEIGHT = 400, 600
GRID_SIZE = 30
GRID_WIDTH = WIDTH // GRID_SIZE
GRID_HEIGHT = HEIGHT // GRID_SIZE
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Tetris Gems")

# 색상 정의
COLORS = [(255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 255, 0)]  # 빨강, 초록, 파랑, 노랑
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)

# 테트리스 블록 모양 (7가지 테트로미노)
SHAPES = [
    [[1, 1, 1, 1]],  # I
    [[1, 1], [1, 1]],  # O
    [[0, 1, 0], [1, 1, 1]],  # T
    [[0, 1, 1], [1, 1, 0]],  # S
    [[1, 1, 0], [0, 1, 1]],  # Z
    [[1, 0, 0], [1, 1, 1]],  # J
    [[0, 0, 1], [1, 1, 1]]   # L
]

# 게임 보드 초기화
board = [[None for _ in range(GRID_WIDTH)] for _ in range(GRID_HEIGHT)]

# 블록 클래스
class Block:
    def __init__(self):
        self.shape = random.choice(SHAPES)
        self.colors = [[random.choice(COLORS) if cell else None for cell in row] for row in self.shape]
        self.x = GRID_WIDTH // 2 - len(self.shape[0]) // 2
        self.y = 0

    def draw(self):
        for i, row in enumerate(self.shape):
            for j, cell in enumerate(row):
                if cell:
                    pygame.draw.rect(screen, self.colors[i][j],
                                     (self.x * GRID_SIZE + j * GRID_SIZE, 
                                      self.y * GRID_SIZE + i * GRID_SIZE, 
                                      GRID_SIZE - 1, GRID_SIZE - 1))

    def move(self, dx, dy):
        self.x += dx
        self.y += dy

    def rotate(self):
        # 90도 회전 (행렬 전치 후 좌우 반전)
        new_shape = [[self.shape[len(self.shape) - 1 - j][i] for j in range(len(self.shape))] 
                     for i in range(len(self.shape[0]))]
        new_colors = [[self.colors[len(self.shape) - 1 - j][i] for j in range(len(self.shape))] 
                      for i in range(len(self.shape[0]))]
        old_shape, old_colors = self.shape, self.colors
        self.shape, self.colors = new_shape, new_colors
        if self.collides():
            self.shape, self.colors = old_shape, old_colors  # 충돌 시 회전 취소

    def collides(self):
        for i, row in enumerate(self.shape):
            for j, cell in enumerate(row):
                if cell:
                    new_x = self.x + j
                    new_y = self.y + i
                    if (new_x < 0 or new_x >= GRID_WIDTH or 
                        new_y >= GRID_HEIGHT or 
                        (new_y >= 0 and board[new_y][new_x] is not None)):
                        return True
        return False

    def place(self):
        for i, row in enumerate(self.shape):
            for j, cell in enumerate(row):
                if cell:
                    board[self.y + i][self.x + j] = self.colors[i][j]

    def drop(self):
        # 블록을 바닥까지 즉시 내리기
        while not self.collides():
            self.y += 1
        self.y -= 1  # 충돌 직전 위치로 되돌리기
        self.place()

# 매치-3 체크 및 제거
def check_matches():
    matches = []
    # 가로 체크
    for i in range(GRID_HEIGHT):
        for j in range(GRID_WIDTH - 2):
            if (board[i][j] and board[i][j + 1] and board[i][j + 2] and
                board[i][j] == board[i][j + 1] == board[i][j + 2]):
                matches.extend([(i, j), (i, j + 1), (i, j + 2)])
    # 세로 체크
    for i in range(GRID_HEIGHT - 2):
        for j in range(GRID_WIDTH):
            if (board[i][j] and board[i + 1][j] and board[i + 2][j] and
                board[i][j] == board[i + 1][j] == board[i + 2][j]):
                matches.extend([(i, j), (i + 1, j), (i + 2, j)])

    # 매치 제거
    for i, j in matches:
        board[i][j] = None

    # 블록 아래로 떨어뜨리기
    for j in range(GRID_WIDTH):
        for i in range(GRID_HEIGHT - 1, -1, -1):
            if board[i][j] is None:
                for k in range(i - 1, -1, -1):
                    if board[k][j] is not None:
                        board[i][j] = board[k][j]
                        board[k][j] = None
                        break

# 메인 게임 루프
def main():
    clock = pygame.time.Clock()
    current_block = Block()
    fall_time = 0
    fall_speed = 500  # 밀리초 단위

    running = True
    while running:
        fall_time += clock.get_rawtime()
        clock.tick(60)

        # 이벤트 처리
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_LEFT and not current_block.collides():
                    current_block.move(-1, 0)
                    if current_block.collides():
                        current_block.move(1, 0)
                if event.key == pygame.K_RIGHT and not current_block.collides():
                    current_block.move(1, 0)
                    if current_block.collides():
                        current_block.move(-1, 0)
                if event.key == pygame.K_DOWN:
                    # 아래 키로 즉시 드롭
                    current_block.drop()
                    check_matches()
                    current_block = Block()
                if event.key == pygame.K_UP:  # 위쪽 화살표로 회전
                    current_block.rotate()

        # 블록 자동 하강
        if fall_time >= fall_speed:
            current_block.move(0, 1)
            if current_block.collides():
                current_block.move(0, -1)
                current_block.place()
                check_matches()
                current_block = Block()
            fall_time = 0

        # 화면 그리기
        screen.fill(BLACK)
        for i in range(GRID_HEIGHT):
            for j in range(GRID_WIDTH):
                if board[i][j]:
                    pygame.draw.rect(screen, board[i][j],
                                     (j * GRID_SIZE, i * GRID_SIZE, GRID_SIZE - 1, GRID_SIZE - 1))
        current_block.draw()
        pygame.display.flip()

    pygame.quit()

if __name__ == "__main__":
    main()

 

 수정된 부분

 

 

 

 5. 위 4에서 나온 코드를 복사하여 code편집기에 붙여넣고 실행합니다.

그러면 아래와 같이 실행되는데, 화살표 아래키를 눌렀을때 즉시 아래로 내려가는 것을 확인할 수 있습니다.

하지만 블록이 아래로 내려가는 속도가 너무 느려 게임이 재미가 없습니다.

 

 

 6. 위 5번의 단점인 블록의 하강 속도를 증가시키키 위해 아래와 같이 그록3에게 요청합니다.

"내려가는 속도가~~"

 

 

소스코드 3( (12-4.py, 최종 코드)

import pygame
import random

# 초기화
pygame.init()

# 화면 설정
WIDTH, HEIGHT = 400, 600
GRID_SIZE = 30
GRID_WIDTH = WIDTH // GRID_SIZE
GRID_HEIGHT = HEIGHT // GRID_SIZE
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Tetris Gems")

# 색상 정의
COLORS = [(255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 255, 0)]  # 빨강, 초록, 파랑, 노랑
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)

# 테트리스 블록 모양 (7가지 테트로미노)
SHAPES = [
    [[1, 1, 1, 1]],  # I
    [[1, 1], [1, 1]],  # O
    [[0, 1, 0], [1, 1, 1]],  # T
    [[0, 1, 1], [1, 1, 0]],  # S
    [[1, 1, 0], [0, 1, 1]],  # Z
    [[1, 0, 0], [1, 1, 1]],  # J
    [[0, 0, 1], [1, 1, 1]]   # L
]

# 게임 보드 초기화
board = [[None for _ in range(GRID_WIDTH)] for _ in range(GRID_HEIGHT)]

# 블록 클래스
class Block:
    def __init__(self):
        self.shape = random.choice(SHAPES)
        self.colors = [[random.choice(COLORS) if cell else None for cell in row] for row in self.shape]
        self.x = GRID_WIDTH // 2 - len(self.shape[0]) // 2
        self.y = 0

    def draw(self):
        for i, row in enumerate(self.shape):
            for j, cell in enumerate(row):
                if cell:
                    pygame.draw.rect(screen, self.colors[i][j],
                                     (self.x * GRID_SIZE + j * GRID_SIZE, 
                                      self.y * GRID_SIZE + i * GRID_SIZE, 
                                      GRID_SIZE - 1, GRID_SIZE - 1))

    def move(self, dx, dy):
        self.x += dx
        self.y += dy

    def rotate(self):
        # 90도 회전 (행렬 전치 후 좌우 반전)
        new_shape = [[self.shape[len(self.shape) - 1 - j][i] for j in range(len(self.shape))] 
                     for i in range(len(self.shape[0]))]
        new_colors = [[self.colors[len(self.shape) - 1 - j][i] for j in range(len(self.shape))] 
                      for i in range(len(self.shape[0]))]
        old_shape, old_colors = self.shape, self.colors
        self.shape, self.colors = new_shape, new_colors
        if self.collides():
            self.shape, self.colors = old_shape, old_colors  # 충돌 시 회전 취소

    def collides(self):
        for i, row in enumerate(self.shape):
            for j, cell in enumerate(row):
                if cell:
                    new_x = self.x + j
                    new_y = self.y + i
                    if (new_x < 0 or new_x >= GRID_WIDTH or 
                        new_y >= GRID_HEIGHT or 
                        (new_y >= 0 and board[new_y][new_x] is not None)):
                        return True
        return False

    def place(self):
        for i, row in enumerate(self.shape):
            for j, cell in enumerate(row):
                if cell:
                    board[self.y + i][self.x + j] = self.colors[i][j]

    def drop(self):
        # 블록을 바닥까지 즉시 내리기
        while not self.collides():
            self.y += 1
        self.y -= 1  # 충돌 직전 위치로 되돌리기
        self.place()

# 매치-3 체크 및 제거
def check_matches():
    matches = []
    # 가로 체크
    for i in range(GRID_HEIGHT):
        for j in range(GRID_WIDTH - 2):
            if (board[i][j] and board[i][j + 1] and board[i][j + 2] and
                board[i][j] == board[i][j + 1] == board[i][j + 2]):
                matches.extend([(i, j), (i, j + 1), (i, j + 2)])
    # 세로 체크
    for i in range(GRID_HEIGHT - 2):
        for j in range(GRID_WIDTH):
            if (board[i][j] and board[i + 1][j] and board[i + 2][j] and
                board[i][j] == board[i + 1][j] == board[i + 2][j]):
                matches.extend([(i, j), (i + 1, j), (i + 2, j)])

    # 매치 제거
    for i, j in matches:
        board[i][j] = None

    # 블록 아래로 떨어뜨리기
    for j in range(GRID_WIDTH):
        for i in range(GRID_HEIGHT - 1, -1, -1):
            if board[i][j] is None:
                for k in range(i - 1, -1, -1):
                    if board[k][j] is not None:
                        board[i][j] = board[k][j]
                        board[k][j] = None
                        break

# 메인 게임 루프
def main():
    clock = pygame.time.Clock()
    current_block = Block()
    fall_time = 0
    fall_speed = 50  # 속도 증가: 500ms -> 200ms (0.2초)

    running = True
    while running:
        fall_time += clock.get_rawtime()
        clock.tick(60)

        # 이벤트 처리
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_LEFT and not current_block.collides():
                    current_block.move(-1, 0)
                    if current_block.collides():
                        current_block.move(1, 0)
                if event.key == pygame.K_RIGHT and not current_block.collides():
                    current_block.move(1, 0)
                    if current_block.collides():
                        current_block.move(-1, 0)
                if event.key == pygame.K_DOWN:
                    # 아래 키로 즉시 드롭
                    current_block.drop()
                    check_matches()
                    current_block = Block()
                if event.key == pygame.K_UP:  # 위쪽 화살표로 회전
                    current_block.rotate()

        # 블록 자동 하강
        if fall_time >= fall_speed:
            current_block.move(0, 1)
            if current_block.collides():
                current_block.move(0, -1)
                current_block.place()
                check_matches()
                current_block = Block()
            fall_time = 0

        # 화면 그리기
        screen.fill(BLACK)
        for i in range(GRID_HEIGHT):
            for j in range(GRID_WIDTH):
                if board[i][j]:
                    pygame.draw.rect(screen, board[i][j],
                                     (j * GRID_SIZE, i * GRID_SIZE, GRID_SIZE - 1, GRID_SIZE - 1))
        current_block.draw()
        pygame.display.flip()

    pygame.quit()

if __name__ == "__main__":
    main()

 

수정된 부분

> 수정된 코드는 fall_speed=500 -> fall_speed=50 로 변경하여 하강 속도를 높였습니다.

 

 

 

 

 

 

 

7.위 6의 수정된 소스코드를 복사 후 code 등에 붙여넣어 실행하면 아래와 같이 실행되는데,

이제 하강 속도가 많이 빨라 졌습니다.

 


 

위와 같이 그록3에게 만들고 싶은 코드를 요청하고, 실행 후 부족한 내용이 있으면 다시 요청하고~~실행하고를

몇번 반복하면 원하는 코드를 제대로 만들어 볼 수 있었습니다.

 

아쉬운 점은 코드를 x.com에서  바로 실행할 수 있었으면 더욱 빠르게 테스트 해볼 수 있었을 것 같은데, 아직 해당 기능은 없는 것으로 보입니다.

 

좋았던 점은 다른 ai챗봇 보다 코드가 월등히 정교하고, 디테일이 굉장한 강점으로 보였습니다.  그 만큼 시간을 절약할 수 있고, 노가다(?)를 덜 할 수 있게 도와주는 AI챗봇으로 보입니다.

 

우리 게시글에 있는 무료AI챗봇 순위에서 당당히 1위를 줄 수 있을 것 같습니다.

https://pagichacha.tistory.com/376

 

현재 그록3 베타는 무료로 풀려 있으므로 많이 사용해 보시면 좋을 것 같습니다.

(단, 사용량 제한은 있으나, 그런데로 충분히 테스트할 정도는 되는것 같습니다.)

 

 

 

오늘은 여기까지이며, 댓글하트는 제가 이글을 지속할 수 있게 해주는 힘이 됩니다.

 

 

 

감사합니다.

 

728x90
반응형
LIST