테트리스에서 회전 버그는 생각보다 빨리 드러납니다. 화면 중앙에서는 잘 돌아가는데 벽 근처에서 갑자기 멈추거나, 쌓인 블록 근처에서 튀는 현상이 대표적입니다. 원인은 대부분 회전 수식이 아니라 검증 순서와 예외 처리에 있습니다.
이 글은 파이썬 테트리스 블록 회전 구현을 안정적으로 만드는 구조를 중심으로 다시 정리한 버전입니다. 핵심은 후보 생성-검증-보정의 3단계 분리입니다.
![[파이썬] 테트리스 블록 회전 구현: 충돌 없이 돌아가게 만드는 체크 순서 2 파이썬 테트리스 블록 회전 구현 본문 이미지 1](https://www.shindeacon.co.kr/wp-content/uploads/2026/04/b1-2.png)
Table of Contents
테트리스 회전은 후보 좌표를 먼저 만들고 유효성 검사를 통과할 때만 적용해야 안전합니다
현재 블록 직접 수정은 피하세요
회전 키 입력 시 현재 좌표를 바로 덮어쓰면 실패했을 때 복구가 복잡해집니다. 회전된 후보를 따로 만들고, 유효성 검사 통과 시에만 반영하는 패턴이 정석입니다.
candidate = rotate_clockwise(current_cells)
if is_valid(candidate, board):
current_cells = candidate
이 패턴을 지키면 회전 실패 시 원 상태가 자동 유지되어 버그가 크게 줄어듭니다.
로그도 후보 기준으로 남기기
디버깅 시에는 현재/후보 좌표를 동시에 찍어야 경계 충돌 원인을 빠르게 찾을 수 있습니다.
유효성 검사는 경계 + 점유를 함께 확인
보드 밖으로 나가는지, 이미 쌓인 블록과 겹치는지를 동시에 검사하세요. 이 두 조건을 분리하면 예외가 쉽게 누락됩니다.
def is_valid(cells, board):
for x, y in cells:
if x < 0 or x >= W or y >= H:
return False
if y >= 0 and board[y][x] == 1:
return False
return True
y < 0는 스폰 구간에서 허용하는 게 일반적입니다.
벽 킥(kick) 없이는 실제 플레이가 불편합니다
벽 근처 회전은 후보 좌표가 살짝 경계를 벗어나기 쉽습니다. 이때 x를 좌우로 조금 이동시켜 재검증하는 킥 로직을 추가하면 체감 품질이 크게 올라갑니다.
for dx in [0, -1, 1, -2, 2]:
shifted = [(x+dx, y) for x, y in candidate]
if is_valid(shifted, board):
current_cells = shifted
break
블록 타입별 킥 정책 분리
I 블록은 폭이 길어 일반 블록과 동일 킥 규칙을 쓰면 어색할 수 있습니다. 타입별 오프셋 테이블을 분리하면 더 자연스럽습니다.
![[파이썬] 테트리스 블록 회전 구현: 충돌 없이 돌아가게 만드는 체크 순서 3 파이썬 테트리스 블록 회전 구현 본문 이미지 2](https://www.shindeacon.co.kr/wp-content/uploads/2026/04/b2-2.png)
회전 중심 좌표를 명확히 정의
O 블록은 회전해도 동일 형태라 중심 오차가 눈에 띄지 않지만, I/T/L 블록은 중심 정의가 다르면 화면에서 위치가 튀는 느낌이 납니다. 블록별 피벗을 명확히 고정해야 합니다.
# 예시: 피벗 기준 상대 좌표
T = [(0,-1), (-1,0), (0,0), (1,0)]
# 회전 시 (x, y) -> (y, -x)
상대좌표 기반 구현은 블록 이동/회전 로직을 단순화해 유지보수에 유리합니다.
자주 발생하는 오류 정리
회전 후 블록이 한 칸 순간이동
피벗 좌표 계산과 보드 좌표 변환 기준이 섞였을 가능성이 큽니다.
쌓인 블록을 뚫고 회전
점유 검사에서 y<0 예외를 과하게 허용했을 수 있습니다.
벽 근처에서 회전 입력 무시
킥 로직이 없거나 오프셋 후보가 너무 적은 경우입니다.
정리
파이썬 테트리스 블록 회전 구현은 수학보다 검증 구조가 먼저입니다. 후보 생성 → 경계/점유 검사 → 킥 보정 순서를 고정하면 벽/적층 구간 버그를 크게 줄일 수 있습니다.