메인 칼럼 개발일지 작업물 연락처

PI로 게임 만들기 ① — Gemini 무료 모델 붙이고 한 방에 뽑아보기

pi를 google provider + gemini-3.1-flash-lite 무료 모델에 연결하고, print 모드로 스네이크 게임을 한 번에 생성해봤습니다. 그리고 첫 결과물에서 발견한 치명적 버그까지.

지금까지 PI의 설치·사용법·서브에이전트·패키지를 다뤘습니다. 이번엔 말로만 하지 말고 실제로 pi에게 게임 하나를 통째로 만들게 해봤습니다. 그것도 돈 한 푼 안 쓰고 — Gemini의 무료 모델 gemini-3.1-flash-lite로요.

이 글부터 3편에 걸쳐, 빈 폴더에서 플레이 가능한 스네이크 게임이 나오기까지의 과정을 명령어와 프롬프트 그대로 기록합니다.


0. 준비물

  • pi 설치 (pi --version → 저는 0.75.5)
  • 빈 작업 폴더 하나
  • Gemini API 키 — 무료 티어로 gemini-3.1-flash-lite 호출 가능

작업 폴더에 .env 파일을 만들고 키를 넣어둡니다.

# .env
GEMINI_API=AIza...   # 본인 키

변수명은 자유롭게 정해도 됩니다. 저는 GEMINI_API로 뒀고, pi에는 --api-key로 직접 넘겼습니다.


1. pi를 Gemini에 연결하기

pi --help를 보면 핵심 플래그가 보입니다.

--provider <name>   Provider name (default: google)
--model <pattern>   Model pattern or ID
--api-key <key>     API key (defaults to env vars)
--print, -p         Non-interactive mode: process prompt and exit

반갑게도 provider 기본값이 google 입니다. 즉 Gemini를 쓰기에 가장 손이 덜 갑니다. 모델 ID만 지정하면 pi가 구글 API로 바로 호출합니다.

먼저 키와 모델이 진짜 도는지 1회 호출로 핑 테스트부터 합니다. .env를 셸로 불러와서:

set -a; . ./.env; set +a   # GEMINI_API 로드

pi -p --provider google --model gemini-3.1-flash-lite \
   --api-key "$GEMINI_API" \
   -nt -ne -ns -np --no-session \
   "Reply with exactly: PI_OK"

출력:

PI_OK

연결 성공. 참고로 위에서 붙인 플래그들은 핑을 가볍게 만들기 위한 것입니다.

  • -nt 도구 끄기(텍스트만)
  • -ne 익스텐션 로드 끄기
  • -ns 스킬 로드 끄기
  • -np 프롬프트 템플릿 끄기
  • --no-session 세션 파일 안 남기기

2. print 모드로 게임을 통째로 만들게 하기

이제 진짜입니다. -p(print) 모드는 프롬프트를 받고 도구를 알아서 쓰고 끝나는 비대화형 모드입니다. 즉 pi가 write/edit/bash 도구로 직접 파일을 만들어냅니다. 스크립트로 자동화하기에 딱이죠.

작업 폴더에서 이렇게 던졌습니다.

pi -p --provider google --model gemini-3.1-flash-lite \
   --api-key "$GEMINI_API" --no-session \
"Create a single self-contained file named index.html implementing a classic Snake game. Strict requirements:
- Pure HTML/CSS/JS in ONE file, no external libraries or CDNs.
- HTML5 canvas, 20x20 grid, square board centered on the page, responsive.
- Controls: Arrow keys AND WASD. Never allow reversing directly into yourself.
- Food spawns at random empty cells; eating grows the snake and adds 10 points.
- Collision with walls or self => game over.
- Game over overlay shows final score and a Restart button; also restart on Space or Enter.
- Game loop around 10 fps.
- Modern dark UI: near-black background, a single bright accent color (#e8734d), rounded board, score at top, title 'PI SNAKE'.
- Write the complete file to index.html now using your write tool."

결과:

The Snake game has been implemented in `index.html`.
...

걸린 시간 약 8초, 만들어진 파일은 index.html 하나(4.4KB). 명령어 하나로 빈 폴더에 게임 파일이 떨어졌습니다.

프롬프트를 영어로 쓴 이유는, 가벼운 모델일수록 영어 지시를 더 또박또박 따라가는 경향이 있어서입니다. 한국어로 해도 되지만 요구사항이 많을 땐 영어가 안정적이었습니다.


3. 첫 결과물 — 보기엔 그럴듯한데

코드를 열어보니 UI는 의외로 깔끔했습니다. 제목, 점수, 액센트 컬러 테두리, Restart 버튼까지. flash-lite치고 인상적이었죠.

그런데 브라우저로 열자마자 이게 떴습니다.

로드하자마자 게임오버가 뜬 첫 결과물

시작하자마자 GAME OVER. 키 한 번 안 눌렀는데요.

원인은 코드를 읽으니 금방 보였습니다. 뱀의 초기 방향이 정지 상태(dx=0, dy=0)인데, 게임 루프는 곧바로 돌기 시작합니다. 그 첫 틱에서 “다음 머리 위치”를 계산하면 현재 머리와 같은 칸이 나오고, 자기 몸과 충돌한 것으로 판정되어 즉시 게임오버가 된 겁니다.

function update() {
    const head = {x: snake[0].x + dx, y: snake[0].y + dy}; // dx,dy=0 → 제자리
    if ( ... || snake.some(p => p.x === head.x && p.y === head.y)) // 자기 자신과 "충돌"
        return gameOver();
    ...
}

가벼운 모델이 자주 내는 종류의 버그입니다. “정지 상태”를 예외 처리해야 한다는 걸 놓친 거죠. 요구사항엔 없었지만 당연히 필요한 디테일.


이번 편 정리

  • pi는 provider 기본값이 google 이라 Gemini 연결이 쉽다.
  • pi -p --model gemini-3.1-flash-lite --api-key ...print 모드 자동화가 된다.
  • flash-lite도 한 번의 프롬프트로 게임 한 파일을 뽑아낸다.
  • 다만 결과물엔 “정지 상태에서 자기 충돌” 같은 현실적인 버그가 섞여 있다.

다음 편에서는 이 버그를 pi에게 다시 시켜서 고치고(읽고-고치는 루프), 그리드·하이스코어 같은 디테일까지 더해 완성합니다. 감사합니다!

김현빈 Developer & Writer

기술, 포스팅 관련 질문, 프로젝트 협업 등 연락주시면 언제든지 회신 드립니다.