메인
home
소프트웨어
home
🎟️

2강. gpt가 만들어주는 streamlit app

gpt에게 요구한 내용들

간단한 게임 만들라고 했음
중간에 에러나서 에러 메세지 복붙함.

최종 화면

최종 코드

import run import streamlit as st import numpy as np from PIL import Image # ========================= # 기본 설정 # ========================= st.set_page_config(page_title="🧱 Streamlit Tetris Mini", layout="centered") st.title("🧱 Streamlit Tetris (Mini)") st.caption("버튼으로 조작: ⬅️➡️ 이동, ⟳ 회전, ⬇️ 내리기 · 자동 낙하 / 점수·레벨 지원") # ========================= # 게임 상수 # ========================= W, H = 10, 20 # 보드 크기 BLOCK = 24 # 렌더링 블록 픽셀 BG = 0 # 빈칸 COLORS = { 0: (20, 20, 25), 1: (0, 255, 255), # I 2: (0, 0, 255), # J 3: (255, 165, 0), # L 4: (255, 255, 0), # O 5: (0, 255, 0), # S 6: (128, 0, 128), # T 7: (255, 0, 0), # Z 8: (90, 90, 100), # 고정된 블록 윤곽 강조(섞임 방지용) } # 7가지 테트리미노(회전 시에는 np.rot90 사용) SHAPES = { 1: np.array([[1,1,1,1]]), # I 2: np.array([[2,0,0], [2,2,2]]), # J 3: np.array([[0,0,3], [3,3,3]]), # L 4: np.array([[4,4], [4,4]]), # O 5: np.array([[0,5,5], [5,5,0]]), # S 6: np.array([[0,6,0], [6,6,6]]), # T 7: np.array([[7,7,0], [0,7,7]]), # Z } # ========================= # 유틸 함수 # ========================= def new_board(): return np.zeros((H, W), dtype=int) def spawn_piece(): kind = np.random.choice(list(SHAPES.keys())) mat = SHAPES[kind].copy() x = (W - mat.shape[1]) // 2 y = 0 return {"kind": kind, "mat": mat, "x": x, "y": y} def collide(board, piece, dx=0, dy=0, rot_mat=None): mat = rot_mat if rot_mat is not None else piece["mat"] x, y = piece["x"] + dx, piece["y"] + dy h, w = mat.shape # 경계 if x < 0 or x + w > W or y + h > H: return True # 충돌 region = board[y:y+h, x:x+w] return np.any((mat > 0) & (region > 0)) def lock_piece(board, piece): mat = piece["mat"] x, y = piece["x"], piece["y"] h, w = mat.shape region = board[y:y+h, x:x+w] mask = mat > 0 region[mask] = mat[mask] board[y:y+h, x:x+w] = region return board def clear_lines(board): full = np.where(np.all(board > 0, axis=1))[0] n = len(full) if n > 0: board = np.delete(board, full, axis=0) board = np.vstack([np.zeros((n, W), dtype=int), board]) return board, n def render(board, piece=None): img_grid = np.zeros((H, W, 3), dtype=np.uint8) # 고정 보드 for val, color in COLORS.items(): if val == 0: continue mask = board == val img_grid[mask] = color # 낙하 중인 조각 오버레이 if piece is not None: mat = piece["mat"]; x = piece["x"]; y = piece["y"] h, w = mat.shape for r in range(h): for c in range(w): if 0 <= y+r < H and 0 <= x+c < W and mat[r, c] > 0: img_grid[y+r, x+c] = COLORS[piece["kind"]] # 최근 채워진 줄 하이라이트(선택)도 가능하지만 간단 버전 img = Image.fromarray(img_grid, mode="RGB") img = img.resize((W*BLOCK, H*BLOCK), resample=Image.NEAREST) return img def try_rotate(board, piece, cw=True): rot = np.rot90(piece["mat"], -1 if cw else 1) # 간단한 벽킥: 제자리, 좌(-1), 우(+1) 시도 for shift in [0, -1, 1, -2, 2]: if not collide(board, piece, dx=shift, dy=0, rot_mat=rot): piece["mat"] = rot piece["x"] += shift return True return False def gravity_interval_ms(level): # 레벨이 올라갈수록 빨라짐(하한선 120ms) return max(120, 700 - (level-1)*60) def score_for_lines(n, level): base = {1:40, 2:100, 3:300, 4:1200}.get(n, 0) return base * level # ========================= # 세션 상태 초기화 # ========================= if "board" not in st.session_state: st.session_state.board = new_board() if "piece" not in st.session_state: st.session_state.piece = spawn_piece() if "next_piece" not in st.session_state: st.session_state.next_piece = spawn_piece() if "score" not in st.session_state: st.session_state.score = 0 if "lines" not in st.session_state: st.session_state.lines = 0 if "level" not in st.session_state: st.session_state.level = 1 if "paused" not in st.session_state: st.session_state.paused = False if "game_over" not in st.session_state: st.session_state.game_over = False if "tick" not in st.session_state: st.session_state.tick = 0 # ========================= # 자동 낙하 (autorefresh) # ========================= interval = gravity_interval_ms(st.session_state.level) if not st.session_state.paused and not st.session_state.game_over: st.experimental_rerun = st.autorefresh(interval=interval, limit=1, key="tetris_autorefresh") # 아래로 한 칸 시도 if not collide(st.session_state.board, st.session_state.piece, dy=1): st.session_state.piece["y"] += 1 else: # 고정/줄삭제/새 조각 st.session_state.board = lock_piece(st.session_state.board, st.session_state.piece) st.session_state.board, cleared = clear_lines(st.session_state.board) if cleared: st.session_state.lines += cleared st.session_state.score += score_for_lines(cleared, st.session_state.level) # 10줄마다 레벨업 new_level = 1 + st.session_state.lines // 10 if new_level > st.session_state.level: st.session_state.level = new_level st.session_state.piece = st.session_state.next_piece st.session_state.next_piece = spawn_piece() # 스폰 즉시 충돌이면 게임오버 if collide(st.session_state.board, st.session_state.piece): st.session_state.game_over = True # ========================= # 좌측: 보드 렌더 # 우측: 정보/다음 조각/조작 # ========================= left, right = st.columns([3, 1]) with left: st.image(render(st.session_state.board, st.session_state.piece), use_column_width=False) with right: st.markdown(f"**Score:** {st.session_state.score}") st.markdown(f"**Lines:** {st.session_state.lines}") st.markdown(f"**Level:** {st.session_state.level}") st.markdown("---") st.markdown("**Next**") # 다음 조각 미니 프리뷰 nxt = st.session_state.next_piece # 미니보드 4x4에 투영 preview = np.zeros((4, 4), dtype=int) m = nxt["mat"]; h, w = m.shape oy = (4 - h)//2; ox = (4 - w)//2 preview[oy:oy+h, ox:ox+w] = np.where(m>0, nxt["kind"], 0) # 미니 렌더 img_prev = Image.new("RGB", (4*BLOCK, 4*BLOCK), (20,20,25)) arr = np.zeros((4, 4, 3), dtype=np.uint8) for val, color in COLORS.items(): if val == 0: continue arr[preview==val] = color Image.fromarray(arr, "RGB").resize((4*BLOCK, 4*BLOCK), Image.NEAREST).paste(img_prev) st.image(Image.fromarray(arr, "RGB").resize((4*BLOCK, 4*BLOCK), Image.NEAREST)) st.markdown("---") c1, c2, c3 = st.columns(3) with c1: if st.button("⬅️ Left"): if not collide(st.session_state.board, st.session_state.piece, dx=-1): st.session_state.piece["x"] -= 1 with c2: if st.button("⟳ Rotate"): try_rotate(st.session_state.board, st.session_state.piece, cw=True) with c3: if st.button("➡️ Right"): if not collide(st.session_state.board, st.session_state.piece, dx=1): st.session_state.piece["x"] += 1 if st.button("⬇️ Soft Drop"): if not collide(st.session_state.board, st.session_state.piece, dy=1): st.session_state.piece["y"] += 1 if st.button("⏬ Hard Drop"): # 끝까지 while not collide(st.session_state.board, st.session_state.piece, dy=1): st.session_state.piece["y"] += 1 # 즉시 고정 st.session_state.board = lock_piece(st.session_state.board, st.session_state.piece) st.session_state.board, cleared = clear_lines(st.session_state.board) if cleared: st.session_state.lines += cleared st.session_state.score += score_for_lines(cleared, st.session_state.level) new_level = 1 + st.session_state.lines // 10 if new_level > st.session_state.level: st.session_state.level = new_level st.session_state.piece = st.session_state.next_piece st.session_state.next_piece = spawn_piece() if collide(st.session_state.board, st.session_state.piece): st.session_state.game_over = True c4, c5 = st.columns(2) with c4: if st.button("⏸ Pause / Resume"): st.session_state.paused = not st.session_state.paused with c5: if st.button("🔄 Reset"): st.session_state.board = new_board() st.session_state.piece = spawn_piece() st.session_state.next_piece = spawn_piece() st.session_state.score = 0 st.session_state.lines = 0 st.session_state.level = 1 st.session_state.paused = False st.session_state.game_over = False if st.session_state.game_over: st.error("💥 Game Over! 🔄 Reset 버튼으로 다시 시작하세요.") # 작은 도움말 with st.expander("도움말"): st.markdown( """ - **자동 낙하**: 레벨에 따라 속도가 빨라집니다. - **조작**: ⬅️➡️ 이동, ⟳ 회전, ⬇️ 소프트드롭, ⏬ 하드드롭, ⏸ 일시정지, 🔄 리셋 - **점수**: 1/2/3/4줄 동시 삭제 시 40/100/300/1200 × 레벨 """ )
Python
복사